Skip to content

Commit ee136e2

Browse files
[Feature] Add serving.http_request to call external functions. (#857)
## What changes are proposed in this pull request? This PR adds the `serving.http_request` function to call `/external-function`. The goal of this function is to make it easy for the AI agent authors to create tools that can make request to external services by invoking the /external-function API where the secrets are stored in UC Connections. This PR is based on PR #852. ## How is this tested? The mixin itself was not tested. Further testing will be conducted by the Model Serving team.
1 parent 8c4264b commit ee136e2

File tree

22 files changed

+766
-322
lines changed

22 files changed

+766
-322
lines changed

.codegen/_openapi_sha

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
779817ed8d63031f5ea761fbd25ee84f38feec0d
1+
05a10af4ed43566968119b43605f0a7fecbe780f

databricks/sdk/credentials_provider.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ def oauth_service_principal(cfg: 'Config') -> Optional[CredentialsProvider]:
167167
oidc = cfg.oidc_endpoints
168168
if oidc is None:
169169
return None
170+
170171
token_source = ClientCredentials(client_id=cfg.client_id,
171172
client_secret=cfg.client_secret,
172173
token_url=oidc.token_endpoint,
@@ -210,16 +211,22 @@ def external_browser(cfg: 'Config') -> Optional[CredentialsProvider]:
210211
credentials = token_cache.load()
211212
if credentials:
212213
# Force a refresh in case the loaded credentials are expired.
213-
credentials.token()
214-
else:
215-
oauth_client = OAuthClient(oidc_endpoints=oidc_endpoints,
216-
client_id=client_id,
217-
redirect_url=redirect_url,
218-
client_secret=client_secret)
219-
consent = oauth_client.initiate_consent()
220-
if not consent:
221-
return None
222-
credentials = consent.launch_external_browser()
214+
# If the refresh fails, rather than throw exception we will initiate a new OAuth login flow.
215+
try:
216+
credentials.token()
217+
return credentials(cfg)
218+
# TODO: we should ideally use more specific exceptions.
219+
except Exception as e:
220+
logger.warning(f'Failed to refresh cached token: {e}. Initiating new OAuth login flow')
221+
222+
oauth_client = OAuthClient(oidc_endpoints=oidc_endpoints,
223+
client_id=client_id,
224+
redirect_url=redirect_url,
225+
client_secret=client_secret)
226+
consent = oauth_client.initiate_consent()
227+
if not consent:
228+
return None
229+
credentials = consent.launch_external_browser()
223230
token_cache.save(credentials)
224231
return credentials(cfg)
225232

databricks/sdk/mixins/open_ai_client.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
from databricks.sdk.service.serving import ServingEndpointsAPI
1+
import json as js
2+
from typing import Dict, Optional
3+
4+
from databricks.sdk.service.serving import (ExternalFunctionRequestHttpMethod,
5+
ExternalFunctionResponse,
6+
ServingEndpointsAPI)
27

38

49
class ServingEndpointsExt(ServingEndpointsAPI):
@@ -50,3 +55,37 @@ def get_langchain_chat_open_ai_client(self, model):
5055
openai_api_base=self._api._cfg.host + "/serving-endpoints",
5156
api_key="no-token", # Passing in a placeholder to pass validations, this will not be used
5257
http_client=self._get_authorized_http_client())
58+
59+
def http_request(self,
60+
conn: str,
61+
method: ExternalFunctionRequestHttpMethod,
62+
path: str,
63+
*,
64+
headers: Optional[Dict[str, str]] = None,
65+
json: Optional[Dict[str, str]] = None,
66+
params: Optional[Dict[str, str]] = None) -> ExternalFunctionResponse:
67+
"""Make external services call using the credentials stored in UC Connection.
68+
**NOTE:** Experimental: This API may change or be removed in a future release without warning.
69+
:param conn: str
70+
The connection name to use. This is required to identify the external connection.
71+
:param method: :class:`ExternalFunctionRequestHttpMethod`
72+
The HTTP method to use (e.g., 'GET', 'POST'). This is required.
73+
:param path: str
74+
The relative path for the API endpoint. This is required.
75+
:param headers: Dict[str,str] (optional)
76+
Additional headers for the request. If not provided, only auth headers from connections would be
77+
passed.
78+
:param json: Dict[str,str] (optional)
79+
JSON payload for the request.
80+
:param params: Dict[str,str] (optional)
81+
Query parameters for the request.
82+
:returns: :class:`ExternalFunctionResponse`
83+
"""
84+
85+
return super.http_request(connection_name=conn,
86+
method=method,
87+
path=path,
88+
headers=js.dumps(headers),
89+
json=js.dumps(json),
90+
params=js.dumps(params),
91+
)

databricks/sdk/service/cleanrooms.py

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

databricks/sdk/service/files.py

Lines changed: 6 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

databricks/sdk/service/jobs.py

Lines changed: 84 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

databricks/sdk/service/oauth2.py

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)