databricks-sdk 0.59.0__py3-none-any.whl → 0.61.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- databricks/sdk/__init__.py +20 -6
- databricks/sdk/credentials_provider.py +2 -2
- databricks/sdk/mixins/files.py +43 -15
- databricks/sdk/mixins/open_ai_client.py +28 -7
- databricks/sdk/oidc.py +6 -2
- databricks/sdk/service/{aibuilder.py → agentbricks.py} +5 -5
- databricks/sdk/service/catalog.py +167 -130
- databricks/sdk/service/cleanrooms.py +524 -30
- databricks/sdk/service/dashboards.py +1 -0
- databricks/sdk/service/database.py +61 -7
- databricks/sdk/service/pipelines.py +2 -0
- databricks/sdk/service/serving.py +1 -0
- databricks/sdk/service/settings.py +1 -0
- databricks/sdk/service/sharing.py +67 -1
- databricks/sdk/service/sql.py +9 -0
- databricks/sdk/version.py +1 -1
- {databricks_sdk-0.59.0.dist-info → databricks_sdk-0.61.0.dist-info}/METADATA +1 -1
- {databricks_sdk-0.59.0.dist-info → databricks_sdk-0.61.0.dist-info}/RECORD +22 -22
- {databricks_sdk-0.59.0.dist-info → databricks_sdk-0.61.0.dist-info}/WHEEL +0 -0
- {databricks_sdk-0.59.0.dist-info → databricks_sdk-0.61.0.dist-info}/licenses/LICENSE +0 -0
- {databricks_sdk-0.59.0.dist-info → databricks_sdk-0.61.0.dist-info}/licenses/NOTICE +0 -0
- {databricks_sdk-0.59.0.dist-info → databricks_sdk-0.61.0.dist-info}/top_level.txt +0 -0
databricks/sdk/__init__.py
CHANGED
|
@@ -13,7 +13,7 @@ from databricks.sdk.mixins.files import DbfsExt, FilesExt
|
|
|
13
13
|
from databricks.sdk.mixins.jobs import JobsExt
|
|
14
14
|
from databricks.sdk.mixins.open_ai_client import ServingEndpointsExt
|
|
15
15
|
from databricks.sdk.mixins.workspace import WorkspaceExt
|
|
16
|
-
from databricks.sdk.service import
|
|
16
|
+
from databricks.sdk.service import agentbricks as pkg_agentbricks
|
|
17
17
|
from databricks.sdk.service import apps as pkg_apps
|
|
18
18
|
from databricks.sdk.service import billing as pkg_billing
|
|
19
19
|
from databricks.sdk.service import catalog as pkg_catalog
|
|
@@ -36,7 +36,7 @@ from databricks.sdk.service import sharing as pkg_sharing
|
|
|
36
36
|
from databricks.sdk.service import sql as pkg_sql
|
|
37
37
|
from databricks.sdk.service import vectorsearch as pkg_vectorsearch
|
|
38
38
|
from databricks.sdk.service import workspace as pkg_workspace
|
|
39
|
-
from databricks.sdk.service.
|
|
39
|
+
from databricks.sdk.service.agentbricks import AgentBricksAPI
|
|
40
40
|
from databricks.sdk.service.apps import AppsAPI
|
|
41
41
|
from databricks.sdk.service.billing import (BillableUsageAPI, BudgetPolicyAPI,
|
|
42
42
|
BudgetsAPI, LogDeliveryAPI,
|
|
@@ -59,7 +59,9 @@ from databricks.sdk.service.catalog import (AccountMetastoreAssignmentsAPI,
|
|
|
59
59
|
TableConstraintsAPI, TablesAPI,
|
|
60
60
|
TemporaryTableCredentialsAPI,
|
|
61
61
|
VolumesAPI, WorkspaceBindingsAPI)
|
|
62
|
-
from databricks.sdk.service.cleanrooms import (
|
|
62
|
+
from databricks.sdk.service.cleanrooms import (CleanRoomAssetRevisionsAPI,
|
|
63
|
+
CleanRoomAssetsAPI,
|
|
64
|
+
CleanRoomAutoApprovalRulesAPI,
|
|
63
65
|
CleanRoomsAPI,
|
|
64
66
|
CleanRoomTaskRunsAPI)
|
|
65
67
|
from databricks.sdk.service.compute import (ClusterPoliciesAPI, ClustersAPI,
|
|
@@ -240,14 +242,16 @@ class WorkspaceClient:
|
|
|
240
242
|
serving_endpoints = ServingEndpointsExt(self._api_client)
|
|
241
243
|
self._access_control = pkg_iam.AccessControlAPI(self._api_client)
|
|
242
244
|
self._account_access_control_proxy = pkg_iam.AccountAccessControlProxyAPI(self._api_client)
|
|
243
|
-
self.
|
|
245
|
+
self._agent_bricks = pkg_agentbricks.AgentBricksAPI(self._api_client)
|
|
244
246
|
self._alerts = pkg_sql.AlertsAPI(self._api_client)
|
|
245
247
|
self._alerts_legacy = pkg_sql.AlertsLegacyAPI(self._api_client)
|
|
246
248
|
self._alerts_v2 = pkg_sql.AlertsV2API(self._api_client)
|
|
247
249
|
self._apps = pkg_apps.AppsAPI(self._api_client)
|
|
248
250
|
self._artifact_allowlists = pkg_catalog.ArtifactAllowlistsAPI(self._api_client)
|
|
249
251
|
self._catalogs = pkg_catalog.CatalogsAPI(self._api_client)
|
|
252
|
+
self._clean_room_asset_revisions = pkg_cleanrooms.CleanRoomAssetRevisionsAPI(self._api_client)
|
|
250
253
|
self._clean_room_assets = pkg_cleanrooms.CleanRoomAssetsAPI(self._api_client)
|
|
254
|
+
self._clean_room_auto_approval_rules = pkg_cleanrooms.CleanRoomAutoApprovalRulesAPI(self._api_client)
|
|
251
255
|
self._clean_room_task_runs = pkg_cleanrooms.CleanRoomTaskRunsAPI(self._api_client)
|
|
252
256
|
self._clean_rooms = pkg_cleanrooms.CleanRoomsAPI(self._api_client)
|
|
253
257
|
self._cluster_policies = pkg_compute.ClusterPoliciesAPI(self._api_client)
|
|
@@ -377,9 +381,9 @@ class WorkspaceClient:
|
|
|
377
381
|
return self._account_access_control_proxy
|
|
378
382
|
|
|
379
383
|
@property
|
|
380
|
-
def
|
|
384
|
+
def agent_bricks(self) -> pkg_agentbricks.AgentBricksAPI:
|
|
381
385
|
"""The Custom LLMs service manages state and powers the UI for the Custom LLM product."""
|
|
382
|
-
return self.
|
|
386
|
+
return self._agent_bricks
|
|
383
387
|
|
|
384
388
|
@property
|
|
385
389
|
def alerts(self) -> pkg_sql.AlertsAPI:
|
|
@@ -411,11 +415,21 @@ class WorkspaceClient:
|
|
|
411
415
|
"""A catalog is the first layer of Unity Catalog’s three-level namespace."""
|
|
412
416
|
return self._catalogs
|
|
413
417
|
|
|
418
|
+
@property
|
|
419
|
+
def clean_room_asset_revisions(self) -> pkg_cleanrooms.CleanRoomAssetRevisionsAPI:
|
|
420
|
+
"""Clean Room Asset Revisions denote new versions of uploaded assets (e.g."""
|
|
421
|
+
return self._clean_room_asset_revisions
|
|
422
|
+
|
|
414
423
|
@property
|
|
415
424
|
def clean_room_assets(self) -> pkg_cleanrooms.CleanRoomAssetsAPI:
|
|
416
425
|
"""Clean room assets are data and code objects — Tables, volumes, and notebooks that are shared with the clean room."""
|
|
417
426
|
return self._clean_room_assets
|
|
418
427
|
|
|
428
|
+
@property
|
|
429
|
+
def clean_room_auto_approval_rules(self) -> pkg_cleanrooms.CleanRoomAutoApprovalRulesAPI:
|
|
430
|
+
"""Clean room auto-approval rules automatically create an approval on your behalf when an asset (e.g."""
|
|
431
|
+
return self._clean_room_auto_approval_rules
|
|
432
|
+
|
|
419
433
|
@property
|
|
420
434
|
def clean_room_task_runs(self) -> pkg_cleanrooms.CleanRoomTaskRunsAPI:
|
|
421
435
|
"""Clean room task runs are the executions of notebooks in a clean room."""
|
|
@@ -331,7 +331,7 @@ def file_oidc(cfg) -> Optional[CredentialsProvider]:
|
|
|
331
331
|
# that provides a Databricks token from an IdTokenSource.
|
|
332
332
|
def _oidc_credentials_provider(cfg, id_token_source: oidc.IdTokenSource) -> Optional[CredentialsProvider]:
|
|
333
333
|
try:
|
|
334
|
-
|
|
334
|
+
id_token_source.id_token() # validate the id_token_source
|
|
335
335
|
except Exception as e:
|
|
336
336
|
logger.debug(f"Failed to get OIDC token: {e}")
|
|
337
337
|
return None
|
|
@@ -341,7 +341,7 @@ def _oidc_credentials_provider(cfg, id_token_source: oidc.IdTokenSource) -> Opti
|
|
|
341
341
|
token_endpoint=cfg.oidc_endpoints.token_endpoint,
|
|
342
342
|
client_id=cfg.client_id,
|
|
343
343
|
account_id=cfg.account_id,
|
|
344
|
-
|
|
344
|
+
id_token_source=id_token_source,
|
|
345
345
|
disable_async=cfg.disable_async_token_refresh,
|
|
346
346
|
)
|
|
347
347
|
|
databricks/sdk/mixins/files.py
CHANGED
|
@@ -28,7 +28,6 @@ from .._base_client import _BaseClient, _RawResponse, _StreamingResponse
|
|
|
28
28
|
from .._property import _cached_property
|
|
29
29
|
from ..config import Config
|
|
30
30
|
from ..errors import AlreadyExists, NotFound
|
|
31
|
-
from ..errors.customizer import _RetryAfterCustomizer
|
|
32
31
|
from ..errors.mapper import _error_mapper
|
|
33
32
|
from ..retries import retried
|
|
34
33
|
from ..service import files
|
|
@@ -577,6 +576,27 @@ class _DbfsPath(_Path):
|
|
|
577
576
|
return f"<_DbfsPath {self._path}>"
|
|
578
577
|
|
|
579
578
|
|
|
579
|
+
class _RetryableException(Exception):
|
|
580
|
+
"""Base class for retryable exceptions in DBFS operations."""
|
|
581
|
+
|
|
582
|
+
def __init__(self, message: str, http_status_code: int):
|
|
583
|
+
super().__init__()
|
|
584
|
+
self.message = message
|
|
585
|
+
self.http_status_code = http_status_code
|
|
586
|
+
|
|
587
|
+
def __str__(self) -> str:
|
|
588
|
+
return f"{self.message} (HTTP Status: {self.http_status_code})"
|
|
589
|
+
|
|
590
|
+
@staticmethod
|
|
591
|
+
def make_error(response: requests.Response) -> "_RetryableException":
|
|
592
|
+
"""Map the response to a retryable exception."""
|
|
593
|
+
|
|
594
|
+
return _RetryableException(
|
|
595
|
+
message=response.text,
|
|
596
|
+
http_status_code=response.status_code,
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
|
|
580
600
|
class DbfsExt(files.DbfsAPI):
|
|
581
601
|
__doc__ = files.DbfsAPI.__doc__
|
|
582
602
|
|
|
@@ -885,7 +905,7 @@ class FilesExt(files.FilesAPI):
|
|
|
885
905
|
timeout=self._config.multipart_upload_single_chunk_upload_timeout_seconds,
|
|
886
906
|
)
|
|
887
907
|
|
|
888
|
-
upload_response = self.
|
|
908
|
+
upload_response = self._retry_cloud_idempotent_operation(perform, rewind)
|
|
889
909
|
|
|
890
910
|
if upload_response.status_code in (200, 201):
|
|
891
911
|
# Chunk upload successful
|
|
@@ -1097,7 +1117,7 @@ class FilesExt(files.FilesAPI):
|
|
|
1097
1117
|
)
|
|
1098
1118
|
|
|
1099
1119
|
try:
|
|
1100
|
-
return self.
|
|
1120
|
+
return self._retry_cloud_idempotent_operation(perform)
|
|
1101
1121
|
except RequestException:
|
|
1102
1122
|
_LOG.warning("Failed to retrieve upload status")
|
|
1103
1123
|
return None
|
|
@@ -1116,7 +1136,7 @@ class FilesExt(files.FilesAPI):
|
|
|
1116
1136
|
# a 503 or 500 response, then you need to resume the interrupted upload from where it left off.
|
|
1117
1137
|
|
|
1118
1138
|
# Let's follow that for all potentially retryable status codes.
|
|
1119
|
-
# Together with the catch block below we replicate the logic in
|
|
1139
|
+
# Together with the catch block below we replicate the logic in _retry_databricks_idempotent_operation().
|
|
1120
1140
|
if upload_response.status_code in self._RETRYABLE_STATUS_CODES:
|
|
1121
1141
|
if retry_count < self._config.multipart_upload_max_retries:
|
|
1122
1142
|
retry_count += 1
|
|
@@ -1243,7 +1263,7 @@ class FilesExt(files.FilesAPI):
|
|
|
1243
1263
|
timeout=self._config.multipart_upload_single_chunk_upload_timeout_seconds,
|
|
1244
1264
|
)
|
|
1245
1265
|
|
|
1246
|
-
abort_response = self.
|
|
1266
|
+
abort_response = self._retry_cloud_idempotent_operation(perform)
|
|
1247
1267
|
|
|
1248
1268
|
if abort_response.status_code not in (200, 201):
|
|
1249
1269
|
raise ValueError(abort_response)
|
|
@@ -1265,7 +1285,7 @@ class FilesExt(files.FilesAPI):
|
|
|
1265
1285
|
timeout=self._config.multipart_upload_single_chunk_upload_timeout_seconds,
|
|
1266
1286
|
)
|
|
1267
1287
|
|
|
1268
|
-
abort_response = self.
|
|
1288
|
+
abort_response = self._retry_cloud_idempotent_operation(perform)
|
|
1269
1289
|
|
|
1270
1290
|
if abort_response.status_code not in (200, 201):
|
|
1271
1291
|
raise ValueError(abort_response)
|
|
@@ -1283,23 +1303,31 @@ class FilesExt(files.FilesAPI):
|
|
|
1283
1303
|
session.mount("http://", http_adapter)
|
|
1284
1304
|
return session
|
|
1285
1305
|
|
|
1286
|
-
def
|
|
1306
|
+
def _retry_cloud_idempotent_operation(
|
|
1287
1307
|
self, operation: Callable[[], requests.Response], before_retry: Callable = None
|
|
1288
1308
|
) -> requests.Response:
|
|
1289
|
-
"""Perform given idempotent operation with necessary retries
|
|
1290
|
-
|
|
1309
|
+
"""Perform given idempotent operation with necessary retries for requests to non Databricks APIs.
|
|
1310
|
+
For cloud APIs, we will retry on network errors and on server response codes.
|
|
1311
|
+
Since operation is idempotent it's safe to retry it for response codes where server state might have changed.
|
|
1291
1312
|
"""
|
|
1292
1313
|
|
|
1293
|
-
def delegate():
|
|
1314
|
+
def delegate() -> requests.Response:
|
|
1294
1315
|
response = operation()
|
|
1295
1316
|
if response.status_code in self._RETRYABLE_STATUS_CODES:
|
|
1296
|
-
|
|
1297
|
-
# this will assign "retry_after_secs" to the attrs, essentially making exception look retryable
|
|
1298
|
-
_RetryAfterCustomizer().customize_error(response, attrs)
|
|
1299
|
-
raise _error_mapper(response, attrs)
|
|
1317
|
+
raise _RetryableException.make_error(response)
|
|
1300
1318
|
else:
|
|
1301
1319
|
return response
|
|
1302
1320
|
|
|
1321
|
+
def extended_is_retryable(e: BaseException) -> Optional[str]:
|
|
1322
|
+
retry_reason_from_base = _BaseClient._is_retryable(e)
|
|
1323
|
+
if retry_reason_from_base is not None:
|
|
1324
|
+
return retry_reason_from_base
|
|
1325
|
+
|
|
1326
|
+
if isinstance(e, _RetryableException):
|
|
1327
|
+
# this is a retriable exception, but not a network error
|
|
1328
|
+
return f"retryable exception (status_code:{e.http_status_code})"
|
|
1329
|
+
return None
|
|
1330
|
+
|
|
1303
1331
|
# following _BaseClient timeout
|
|
1304
1332
|
retry_timeout_seconds = self._config.retry_timeout_seconds or 300
|
|
1305
1333
|
|
|
@@ -1307,7 +1335,7 @@ class FilesExt(files.FilesAPI):
|
|
|
1307
1335
|
timeout=timedelta(seconds=retry_timeout_seconds),
|
|
1308
1336
|
# also retry on network errors (connection error, connection timeout)
|
|
1309
1337
|
# where we believe request didn't reach the server
|
|
1310
|
-
is_retryable=
|
|
1338
|
+
is_retryable=extended_is_retryable,
|
|
1311
1339
|
before_retry=before_retry,
|
|
1312
1340
|
)(delegate)()
|
|
1313
1341
|
|
|
@@ -4,6 +4,7 @@ from typing import Dict, Optional
|
|
|
4
4
|
from requests import Response
|
|
5
5
|
|
|
6
6
|
from databricks.sdk.service.serving import (ExternalFunctionRequestHttpMethod,
|
|
7
|
+
HttpRequestResponse,
|
|
7
8
|
ServingEndpointsAPI)
|
|
8
9
|
|
|
9
10
|
|
|
@@ -88,15 +89,30 @@ class ServingEndpointsExt(ServingEndpointsAPI):
|
|
|
88
89
|
"""
|
|
89
90
|
response = Response()
|
|
90
91
|
response.status_code = 200
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
|
|
93
|
+
# We currently don't call super.http_request because we need to pass in response_headers
|
|
94
|
+
# This is a temporary fix to get the headers we need for the MCP session id
|
|
95
|
+
# TODO: Remove this once we have a better way to get back the response headers
|
|
96
|
+
headers_to_capture = ["mcp-session-id"]
|
|
97
|
+
res = self._api.do(
|
|
98
|
+
"POST",
|
|
99
|
+
"/api/2.0/external-function",
|
|
100
|
+
body={
|
|
101
|
+
"connection_name": conn,
|
|
102
|
+
"method": method.value,
|
|
103
|
+
"path": path,
|
|
104
|
+
"headers": js.dumps(headers) if headers is not None else None,
|
|
105
|
+
"json": js.dumps(json) if json is not None else None,
|
|
106
|
+
"params": js.dumps(params) if params is not None else None,
|
|
107
|
+
},
|
|
108
|
+
headers={"Accept": "text/plain", "Content-Type": "application/json"},
|
|
109
|
+
raw=True,
|
|
110
|
+
response_headers=headers_to_capture,
|
|
98
111
|
)
|
|
99
112
|
|
|
113
|
+
# Create HttpRequestResponse from the raw response
|
|
114
|
+
server_response = HttpRequestResponse.from_dict(res)
|
|
115
|
+
|
|
100
116
|
# Read the content from the HttpRequestResponse object
|
|
101
117
|
if hasattr(server_response, "contents") and hasattr(server_response.contents, "read"):
|
|
102
118
|
raw_content = server_response.contents.read() # Read the bytes
|
|
@@ -109,4 +125,9 @@ class ServingEndpointsExt(ServingEndpointsAPI):
|
|
|
109
125
|
else:
|
|
110
126
|
raise ValueError("Contents must be bytes.")
|
|
111
127
|
|
|
128
|
+
# Copy headers from raw response to Response
|
|
129
|
+
for header_name in headers_to_capture:
|
|
130
|
+
if header_name in res:
|
|
131
|
+
response.headers[header_name] = res[header_name]
|
|
132
|
+
|
|
112
133
|
return response
|
databricks/sdk/oidc.py
CHANGED
|
@@ -188,14 +188,18 @@ class DatabricksOidcTokenSource(oauth.TokenSource):
|
|
|
188
188
|
logger.debug("Client ID provided, authenticating with Workload Identity Federation")
|
|
189
189
|
|
|
190
190
|
id_token = self._id_token_source.id_token()
|
|
191
|
+
return self._exchange_id_token(id_token)
|
|
191
192
|
|
|
193
|
+
# This function is used to create the OAuth client.
|
|
194
|
+
# It exists to make it easier to test.
|
|
195
|
+
def _exchange_id_token(self, id_token: IdToken) -> oauth.Token:
|
|
192
196
|
client = oauth.ClientCredentials(
|
|
193
197
|
client_id=self._client_id,
|
|
194
|
-
client_secret="", #
|
|
198
|
+
client_secret="", # there is no (rotatable) secrets in the OIDC flow
|
|
195
199
|
token_url=self._token_endpoint,
|
|
196
200
|
endpoint_params={
|
|
197
201
|
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
|
|
198
|
-
"subject_token": id_token,
|
|
202
|
+
"subject_token": id_token.jwt,
|
|
199
203
|
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
|
|
200
204
|
},
|
|
201
205
|
scopes=["all-apis"],
|
|
@@ -23,9 +23,6 @@ class CustomLlm:
|
|
|
23
23
|
instructions: str
|
|
24
24
|
"""Instructions for the custom LLM to follow"""
|
|
25
25
|
|
|
26
|
-
optimization_state: State
|
|
27
|
-
"""If optimization is kicked off, tracks the state of the custom LLM"""
|
|
28
|
-
|
|
29
26
|
agent_artifact_path: Optional[str] = None
|
|
30
27
|
|
|
31
28
|
creation_time: Optional[str] = None
|
|
@@ -45,6 +42,9 @@ class CustomLlm:
|
|
|
45
42
|
|
|
46
43
|
id: Optional[str] = None
|
|
47
44
|
|
|
45
|
+
optimization_state: Optional[State] = None
|
|
46
|
+
"""If optimization is kicked off, tracks the state of the custom LLM"""
|
|
47
|
+
|
|
48
48
|
def as_dict(self) -> dict:
|
|
49
49
|
"""Serializes the CustomLlm into a dictionary suitable for use as a JSON request body."""
|
|
50
50
|
body = {}
|
|
@@ -190,7 +190,7 @@ class Table:
|
|
|
190
190
|
)
|
|
191
191
|
|
|
192
192
|
|
|
193
|
-
class
|
|
193
|
+
class AgentBricksAPI:
|
|
194
194
|
"""The Custom LLMs service manages state and powers the UI for the Custom LLM product."""
|
|
195
195
|
|
|
196
196
|
def __init__(self, api_client):
|
|
@@ -270,7 +270,7 @@ class AiBuilderAPI:
|
|
|
270
270
|
"Accept": "application/json",
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
-
self._api.do("DELETE", f"/api/2.0/custom-
|
|
273
|
+
self._api.do("DELETE", f"/api/2.0/custom-llms/{id}", headers=headers)
|
|
274
274
|
|
|
275
275
|
def get_custom_llm(self, id: str) -> CustomLlm:
|
|
276
276
|
"""Get a Custom LLM.
|