beamlit 0.0.18__py3-none-any.whl → 0.0.20rc1__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.
- beamlit/api/agents/create_agent.py +14 -9
- beamlit/api/agents/get_agent_deployment.py +22 -1
- beamlit/api/agents/update_agent.py +14 -9
- beamlit/api/functions/create_function.py +14 -9
- beamlit/api/functions/update_function.py +14 -9
- beamlit/api/{authentication_providers/list_organizations_for_authentication_provider.py → integrations/create_integration_connection.py} +45 -41
- beamlit/api/integrations/delete_integration_connection.py +158 -0
- beamlit/api/integrations/get_integration.py +97 -0
- beamlit/api/integrations/get_integration_connection.py +154 -0
- beamlit/api/integrations/get_integration_connection_model.py +97 -0
- beamlit/api/integrations/get_integration_model.py +97 -0
- beamlit/api/integrations/list_integration_connection_models.py +97 -0
- beamlit/api/{authentication_providers/list_models_for_authentication_provider.py → integrations/list_integration_connections.py} +24 -48
- beamlit/api/integrations/list_integration_models.py +97 -0
- beamlit/api/integrations/update_integration_connection.py +180 -0
- beamlit/authentication/__init__.py +2 -1
- beamlit/authentication/authentication.py +3 -0
- beamlit/authentication/clientcredentials.py +99 -0
- beamlit/authentication/credentials.py +7 -1
- beamlit/authentication/device_mode.py +0 -2
- beamlit/common/generate.py +166 -0
- beamlit/common/logger.py +30 -0
- beamlit/common/settings.py +112 -0
- beamlit/common/utils.py +13 -0
- beamlit/models/__init__.py +50 -24
- beamlit/models/agent_deployment.py +117 -49
- beamlit/models/{agent_deployment_configuration.py → agent_deployment_configuration_type_0.py} +5 -5
- beamlit/models/agent_deployment_history.py +46 -13
- beamlit/models/agent_deployment_history_event.py +78 -22
- beamlit/models/{function_deployment_pod_template.py → agent_deployment_pod_template_type_0.py} +5 -5
- beamlit/models/agent_with_deployments.py +174 -0
- beamlit/models/deployment_configurations.py +19 -6
- beamlit/models/deployment_serverless_config_type_0.py +218 -0
- beamlit/models/environment_metrics.py +19 -0
- beamlit/models/function_deployment.py +110 -42
- beamlit/models/{function_deployment_configuration.py → function_deployment_configuration_type_0.py} +5 -5
- beamlit/models/{agent_deployment_pod_template.py → function_deployment_pod_template_type_0.py} +5 -5
- beamlit/models/function_with_deployments.py +174 -0
- beamlit/models/increase_and_rate_metric.py +102 -0
- beamlit/models/integration.py +196 -0
- beamlit/models/integration_config.py +43 -0
- beamlit/models/integration_connection.py +196 -0
- beamlit/models/integration_connection_config.py +43 -0
- beamlit/models/integration_connection_secret.py +59 -0
- beamlit/models/integration_model.py +142 -0
- beamlit/models/integration_secret.py +59 -0
- beamlit/models/metrics.py +61 -20
- beamlit/models/model_deployment.py +96 -64
- beamlit/models/{model_deployment_pod_template.py → model_deployment_pod_template_type_0.py} +5 -5
- beamlit/models/policy.py +8 -34
- beamlit/models/provider_config.py +0 -9
- beamlit/models/resource_deployment_metrics.py +231 -36
- beamlit/models/resource_deployment_metrics_inference_per_region_type_0.py +75 -0
- beamlit/models/{resource_deployment_metrics_inference_per_second_per_region.py → resource_deployment_metrics_inference_per_second_per_region_type_0.py} +5 -5
- beamlit/models/resource_deployment_metrics_query_per_region_per_code_type_0.py +73 -0
- beamlit/models/{resource_deployment_metrics_query_per_second_per_region_per_code.py → resource_deployment_metrics_query_per_second_per_region_per_code_type_0.py} +5 -5
- beamlit/models/resource_metrics.py +36 -0
- beamlit/models/runtime.py +29 -12
- beamlit/models/{runtime_readiness_probe.py → runtime_readiness_probe_type_0.py} +5 -5
- beamlit/models/store_agent.py +29 -13
- beamlit/models/{store_agent_labels.py → store_agent_labels_type_0.py} +5 -5
- beamlit/models/store_configuration.py +15 -4
- beamlit/models/store_configuration_option.py +18 -5
- beamlit/models/store_function.py +29 -13
- beamlit/models/{store_function_labels.py → store_function_labels_type_0.py} +5 -5
- beamlit/run.py +49 -0
- {beamlit-0.0.18.dist-info → beamlit-0.0.20rc1.dist-info}/METADATA +5 -2
- {beamlit-0.0.18.dist-info → beamlit-0.0.20rc1.dist-info}/RECORD +70 -46
- beamlit/api/authentication_providers/get_model_with_repo_for_authentication_provider.py +0 -184
- beamlit/models/deployment_serverless_config.py +0 -129
- beamlit/models/labels.py +0 -43
- /beamlit/api/{authentication_providers → integrations}/__init__.py +0 -0
- {beamlit-0.0.18.dist-info → beamlit-0.0.20rc1.dist-info}/WHEEL +0 -0
@@ -5,16 +5,14 @@ import httpx
|
|
5
5
|
|
6
6
|
from ... import errors
|
7
7
|
from ...client import AuthenticatedClient, Client
|
8
|
-
from ...models.
|
8
|
+
from ...models.integration_connection import IntegrationConnection
|
9
9
|
from ...types import Response
|
10
10
|
|
11
11
|
|
12
|
-
def _get_kwargs(
|
13
|
-
authentication_provider_name: str,
|
14
|
-
) -> dict[str, Any]:
|
12
|
+
def _get_kwargs() -> dict[str, Any]:
|
15
13
|
_kwargs: dict[str, Any] = {
|
16
14
|
"method": "get",
|
17
|
-
"url":
|
15
|
+
"url": "/integrations/connections",
|
18
16
|
}
|
19
17
|
|
20
18
|
return _kwargs
|
@@ -22,12 +20,12 @@ def _get_kwargs(
|
|
22
20
|
|
23
21
|
def _parse_response(
|
24
22
|
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
25
|
-
) -> Optional[List["
|
23
|
+
) -> Optional[List["IntegrationConnection"]]:
|
26
24
|
if response.status_code == 200:
|
27
25
|
response_200 = []
|
28
26
|
_response_200 = response.json()
|
29
27
|
for response_200_item_data in _response_200:
|
30
|
-
response_200_item =
|
28
|
+
response_200_item = IntegrationConnection.from_dict(response_200_item_data)
|
31
29
|
|
32
30
|
response_200.append(response_200_item)
|
33
31
|
|
@@ -40,7 +38,7 @@ def _parse_response(
|
|
40
38
|
|
41
39
|
def _build_response(
|
42
40
|
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
43
|
-
) -> Response[List["
|
41
|
+
) -> Response[List["IntegrationConnection"]]:
|
44
42
|
return Response(
|
45
43
|
status_code=HTTPStatus(response.status_code),
|
46
44
|
content=response.content,
|
@@ -50,28 +48,22 @@ def _build_response(
|
|
50
48
|
|
51
49
|
|
52
50
|
def sync_detailed(
|
53
|
-
authentication_provider_name: str,
|
54
51
|
*,
|
55
52
|
client: AuthenticatedClient,
|
56
|
-
) -> Response[List["
|
57
|
-
"""List
|
53
|
+
) -> Response[List["IntegrationConnection"]]:
|
54
|
+
"""List integrations connections
|
58
55
|
|
59
|
-
Returns a list of all
|
60
|
-
|
61
|
-
Args:
|
62
|
-
authentication_provider_name (str):
|
56
|
+
Returns a list of all connections integrations in the workspace.
|
63
57
|
|
64
58
|
Raises:
|
65
59
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
66
60
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
67
61
|
|
68
62
|
Returns:
|
69
|
-
Response[List['
|
63
|
+
Response[List['IntegrationConnection']]
|
70
64
|
"""
|
71
65
|
|
72
|
-
kwargs = _get_kwargs(
|
73
|
-
authentication_provider_name=authentication_provider_name,
|
74
|
-
)
|
66
|
+
kwargs = _get_kwargs()
|
75
67
|
|
76
68
|
response = client.get_httpx_client().request(
|
77
69
|
**kwargs,
|
@@ -81,54 +73,43 @@ def sync_detailed(
|
|
81
73
|
|
82
74
|
|
83
75
|
def sync(
|
84
|
-
authentication_provider_name: str,
|
85
76
|
*,
|
86
77
|
client: AuthenticatedClient,
|
87
|
-
) -> Optional[List["
|
88
|
-
"""List
|
89
|
-
|
90
|
-
Returns a list of all models for an integration by ID.
|
78
|
+
) -> Optional[List["IntegrationConnection"]]:
|
79
|
+
"""List integrations connections
|
91
80
|
|
92
|
-
|
93
|
-
authentication_provider_name (str):
|
81
|
+
Returns a list of all connections integrations in the workspace.
|
94
82
|
|
95
83
|
Raises:
|
96
84
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
97
85
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
98
86
|
|
99
87
|
Returns:
|
100
|
-
List['
|
88
|
+
List['IntegrationConnection']
|
101
89
|
"""
|
102
90
|
|
103
91
|
return sync_detailed(
|
104
|
-
authentication_provider_name=authentication_provider_name,
|
105
92
|
client=client,
|
106
93
|
).parsed
|
107
94
|
|
108
95
|
|
109
96
|
async def asyncio_detailed(
|
110
|
-
authentication_provider_name: str,
|
111
97
|
*,
|
112
98
|
client: AuthenticatedClient,
|
113
|
-
) -> Response[List["
|
114
|
-
"""List
|
99
|
+
) -> Response[List["IntegrationConnection"]]:
|
100
|
+
"""List integrations connections
|
115
101
|
|
116
|
-
Returns a list of all
|
117
|
-
|
118
|
-
Args:
|
119
|
-
authentication_provider_name (str):
|
102
|
+
Returns a list of all connections integrations in the workspace.
|
120
103
|
|
121
104
|
Raises:
|
122
105
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
123
106
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
124
107
|
|
125
108
|
Returns:
|
126
|
-
Response[List['
|
109
|
+
Response[List['IntegrationConnection']]
|
127
110
|
"""
|
128
111
|
|
129
|
-
kwargs = _get_kwargs(
|
130
|
-
authentication_provider_name=authentication_provider_name,
|
131
|
-
)
|
112
|
+
kwargs = _get_kwargs()
|
132
113
|
|
133
114
|
response = await client.get_async_httpx_client().request(**kwargs)
|
134
115
|
|
@@ -136,28 +117,23 @@ async def asyncio_detailed(
|
|
136
117
|
|
137
118
|
|
138
119
|
async def asyncio(
|
139
|
-
authentication_provider_name: str,
|
140
120
|
*,
|
141
121
|
client: AuthenticatedClient,
|
142
|
-
) -> Optional[List["
|
143
|
-
"""List
|
144
|
-
|
145
|
-
Returns a list of all models for an integration by ID.
|
122
|
+
) -> Optional[List["IntegrationConnection"]]:
|
123
|
+
"""List integrations connections
|
146
124
|
|
147
|
-
|
148
|
-
authentication_provider_name (str):
|
125
|
+
Returns a list of all connections integrations in the workspace.
|
149
126
|
|
150
127
|
Raises:
|
151
128
|
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
152
129
|
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
153
130
|
|
154
131
|
Returns:
|
155
|
-
List['
|
132
|
+
List['IntegrationConnection']
|
156
133
|
"""
|
157
134
|
|
158
135
|
return (
|
159
136
|
await asyncio_detailed(
|
160
|
-
authentication_provider_name=authentication_provider_name,
|
161
137
|
client=client,
|
162
138
|
)
|
163
139
|
).parsed
|
@@ -0,0 +1,97 @@
|
|
1
|
+
from http import HTTPStatus
|
2
|
+
from typing import Any, Optional, Union
|
3
|
+
|
4
|
+
import httpx
|
5
|
+
|
6
|
+
from ... import errors
|
7
|
+
from ...client import AuthenticatedClient, Client
|
8
|
+
from ...types import Response
|
9
|
+
|
10
|
+
|
11
|
+
def _get_kwargs(
|
12
|
+
integration_name: str,
|
13
|
+
) -> dict[str, Any]:
|
14
|
+
_kwargs: dict[str, Any] = {
|
15
|
+
"method": "get",
|
16
|
+
"url": f"/integrations/{integration_name}/models",
|
17
|
+
}
|
18
|
+
|
19
|
+
return _kwargs
|
20
|
+
|
21
|
+
|
22
|
+
def _parse_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Optional[Any]:
|
23
|
+
if response.status_code == 200:
|
24
|
+
return None
|
25
|
+
if client.raise_on_unexpected_status:
|
26
|
+
raise errors.UnexpectedStatus(response.status_code, response.content)
|
27
|
+
else:
|
28
|
+
return None
|
29
|
+
|
30
|
+
|
31
|
+
def _build_response(*, client: Union[AuthenticatedClient, Client], response: httpx.Response) -> Response[Any]:
|
32
|
+
return Response(
|
33
|
+
status_code=HTTPStatus(response.status_code),
|
34
|
+
content=response.content,
|
35
|
+
headers=response.headers,
|
36
|
+
parsed=_parse_response(client=client, response=response),
|
37
|
+
)
|
38
|
+
|
39
|
+
|
40
|
+
def sync_detailed(
|
41
|
+
integration_name: str,
|
42
|
+
*,
|
43
|
+
client: AuthenticatedClient,
|
44
|
+
) -> Response[Any]:
|
45
|
+
"""List integration models
|
46
|
+
|
47
|
+
Returns a list of all models for an integration.
|
48
|
+
|
49
|
+
Args:
|
50
|
+
integration_name (str):
|
51
|
+
|
52
|
+
Raises:
|
53
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
54
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
Response[Any]
|
58
|
+
"""
|
59
|
+
|
60
|
+
kwargs = _get_kwargs(
|
61
|
+
integration_name=integration_name,
|
62
|
+
)
|
63
|
+
|
64
|
+
response = client.get_httpx_client().request(
|
65
|
+
**kwargs,
|
66
|
+
)
|
67
|
+
|
68
|
+
return _build_response(client=client, response=response)
|
69
|
+
|
70
|
+
|
71
|
+
async def asyncio_detailed(
|
72
|
+
integration_name: str,
|
73
|
+
*,
|
74
|
+
client: AuthenticatedClient,
|
75
|
+
) -> Response[Any]:
|
76
|
+
"""List integration models
|
77
|
+
|
78
|
+
Returns a list of all models for an integration.
|
79
|
+
|
80
|
+
Args:
|
81
|
+
integration_name (str):
|
82
|
+
|
83
|
+
Raises:
|
84
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
85
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
86
|
+
|
87
|
+
Returns:
|
88
|
+
Response[Any]
|
89
|
+
"""
|
90
|
+
|
91
|
+
kwargs = _get_kwargs(
|
92
|
+
integration_name=integration_name,
|
93
|
+
)
|
94
|
+
|
95
|
+
response = await client.get_async_httpx_client().request(**kwargs)
|
96
|
+
|
97
|
+
return _build_response(client=client, response=response)
|
@@ -0,0 +1,180 @@
|
|
1
|
+
from http import HTTPStatus
|
2
|
+
from typing import Any, Optional, Union
|
3
|
+
|
4
|
+
import httpx
|
5
|
+
|
6
|
+
from ... import errors
|
7
|
+
from ...client import AuthenticatedClient, Client
|
8
|
+
from ...models.integration_connection import IntegrationConnection
|
9
|
+
from ...types import Response
|
10
|
+
|
11
|
+
|
12
|
+
def _get_kwargs(
|
13
|
+
connection_name: str,
|
14
|
+
*,
|
15
|
+
body: IntegrationConnection,
|
16
|
+
) -> dict[str, Any]:
|
17
|
+
headers: dict[str, Any] = {}
|
18
|
+
|
19
|
+
_kwargs: dict[str, Any] = {
|
20
|
+
"method": "put",
|
21
|
+
"url": f"/integrations/connections/{connection_name}",
|
22
|
+
}
|
23
|
+
|
24
|
+
_body = body.to_dict()
|
25
|
+
|
26
|
+
_kwargs["json"] = _body
|
27
|
+
headers["Content-Type"] = "application/json"
|
28
|
+
|
29
|
+
_kwargs["headers"] = headers
|
30
|
+
return _kwargs
|
31
|
+
|
32
|
+
|
33
|
+
def _parse_response(
|
34
|
+
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
35
|
+
) -> Optional[IntegrationConnection]:
|
36
|
+
if response.status_code == 200:
|
37
|
+
response_200 = IntegrationConnection.from_dict(response.json())
|
38
|
+
|
39
|
+
return response_200
|
40
|
+
if client.raise_on_unexpected_status:
|
41
|
+
raise errors.UnexpectedStatus(response.status_code, response.content)
|
42
|
+
else:
|
43
|
+
return None
|
44
|
+
|
45
|
+
|
46
|
+
def _build_response(
|
47
|
+
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
|
48
|
+
) -> Response[IntegrationConnection]:
|
49
|
+
return Response(
|
50
|
+
status_code=HTTPStatus(response.status_code),
|
51
|
+
content=response.content,
|
52
|
+
headers=response.headers,
|
53
|
+
parsed=_parse_response(client=client, response=response),
|
54
|
+
)
|
55
|
+
|
56
|
+
|
57
|
+
def sync_detailed(
|
58
|
+
connection_name: str,
|
59
|
+
*,
|
60
|
+
client: Union[AuthenticatedClient, Client],
|
61
|
+
body: IntegrationConnection,
|
62
|
+
) -> Response[IntegrationConnection]:
|
63
|
+
"""Update integration connection
|
64
|
+
|
65
|
+
Update an integration connection by integration name and connection name.
|
66
|
+
|
67
|
+
Args:
|
68
|
+
connection_name (str):
|
69
|
+
body (IntegrationConnection): Integration Connection
|
70
|
+
|
71
|
+
Raises:
|
72
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
73
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
74
|
+
|
75
|
+
Returns:
|
76
|
+
Response[IntegrationConnection]
|
77
|
+
"""
|
78
|
+
|
79
|
+
kwargs = _get_kwargs(
|
80
|
+
connection_name=connection_name,
|
81
|
+
body=body,
|
82
|
+
)
|
83
|
+
|
84
|
+
response = client.get_httpx_client().request(
|
85
|
+
**kwargs,
|
86
|
+
)
|
87
|
+
|
88
|
+
return _build_response(client=client, response=response)
|
89
|
+
|
90
|
+
|
91
|
+
def sync(
|
92
|
+
connection_name: str,
|
93
|
+
*,
|
94
|
+
client: Union[AuthenticatedClient, Client],
|
95
|
+
body: IntegrationConnection,
|
96
|
+
) -> Optional[IntegrationConnection]:
|
97
|
+
"""Update integration connection
|
98
|
+
|
99
|
+
Update an integration connection by integration name and connection name.
|
100
|
+
|
101
|
+
Args:
|
102
|
+
connection_name (str):
|
103
|
+
body (IntegrationConnection): Integration Connection
|
104
|
+
|
105
|
+
Raises:
|
106
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
107
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
108
|
+
|
109
|
+
Returns:
|
110
|
+
IntegrationConnection
|
111
|
+
"""
|
112
|
+
|
113
|
+
return sync_detailed(
|
114
|
+
connection_name=connection_name,
|
115
|
+
client=client,
|
116
|
+
body=body,
|
117
|
+
).parsed
|
118
|
+
|
119
|
+
|
120
|
+
async def asyncio_detailed(
|
121
|
+
connection_name: str,
|
122
|
+
*,
|
123
|
+
client: Union[AuthenticatedClient, Client],
|
124
|
+
body: IntegrationConnection,
|
125
|
+
) -> Response[IntegrationConnection]:
|
126
|
+
"""Update integration connection
|
127
|
+
|
128
|
+
Update an integration connection by integration name and connection name.
|
129
|
+
|
130
|
+
Args:
|
131
|
+
connection_name (str):
|
132
|
+
body (IntegrationConnection): Integration Connection
|
133
|
+
|
134
|
+
Raises:
|
135
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
136
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
137
|
+
|
138
|
+
Returns:
|
139
|
+
Response[IntegrationConnection]
|
140
|
+
"""
|
141
|
+
|
142
|
+
kwargs = _get_kwargs(
|
143
|
+
connection_name=connection_name,
|
144
|
+
body=body,
|
145
|
+
)
|
146
|
+
|
147
|
+
response = await client.get_async_httpx_client().request(**kwargs)
|
148
|
+
|
149
|
+
return _build_response(client=client, response=response)
|
150
|
+
|
151
|
+
|
152
|
+
async def asyncio(
|
153
|
+
connection_name: str,
|
154
|
+
*,
|
155
|
+
client: Union[AuthenticatedClient, Client],
|
156
|
+
body: IntegrationConnection,
|
157
|
+
) -> Optional[IntegrationConnection]:
|
158
|
+
"""Update integration connection
|
159
|
+
|
160
|
+
Update an integration connection by integration name and connection name.
|
161
|
+
|
162
|
+
Args:
|
163
|
+
connection_name (str):
|
164
|
+
body (IntegrationConnection): Integration Connection
|
165
|
+
|
166
|
+
Raises:
|
167
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
168
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
169
|
+
|
170
|
+
Returns:
|
171
|
+
IntegrationConnection
|
172
|
+
"""
|
173
|
+
|
174
|
+
return (
|
175
|
+
await asyncio_detailed(
|
176
|
+
connection_name=connection_name,
|
177
|
+
client=client,
|
178
|
+
body=body,
|
179
|
+
)
|
180
|
+
).parsed
|
@@ -2,7 +2,7 @@ from .apikey import ApiKeyProvider
|
|
2
2
|
from .authentication import (PublicProvider, RunClientWithCredentials,
|
3
3
|
new_client_with_credentials)
|
4
4
|
from .credentials import (Config, ContextConfig, Credentials, WorkspaceConfig,
|
5
|
-
load_credentials)
|
5
|
+
load_credentials, load_credentials_from_config)
|
6
6
|
from .device_mode import (BearerToken, DeviceLogin, DeviceLoginFinalizeRequest,
|
7
7
|
DeviceLoginFinalizeResponse, DeviceLoginResponse)
|
8
8
|
|
@@ -16,6 +16,7 @@ __all__ = (
|
|
16
16
|
"Credentials",
|
17
17
|
"WorkspaceConfig",
|
18
18
|
"load_credentials",
|
19
|
+
"load_credentials_from_config",
|
19
20
|
"BearerToken",
|
20
21
|
"DeviceLogin",
|
21
22
|
"DeviceLoginFinalizeRequest",
|
@@ -5,6 +5,7 @@ from httpx import Auth, Request, Response
|
|
5
5
|
|
6
6
|
from ..client import AuthenticatedClient
|
7
7
|
from .apikey import ApiKeyProvider
|
8
|
+
from .clientcredentials import ClientCredentials
|
8
9
|
from .credentials import Credentials
|
9
10
|
from .device_mode import BearerToken
|
10
11
|
|
@@ -29,6 +30,8 @@ def new_client_with_credentials(config: RunClientWithCredentials):
|
|
29
30
|
provider = ApiKeyProvider(config.credentials, config.workspace)
|
30
31
|
elif config.credentials.access_token:
|
31
32
|
provider = BearerToken(config.credentials, config.workspace, config.api_url)
|
33
|
+
elif config.credentials.client_credentials:
|
34
|
+
provider = ClientCredentials(config.credentials, config.workspace, config.api_url)
|
32
35
|
else:
|
33
36
|
provider = PublicProvider()
|
34
37
|
|
@@ -0,0 +1,99 @@
|
|
1
|
+
import base64
|
2
|
+
import json
|
3
|
+
import time
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from typing import Generator, Optional
|
6
|
+
|
7
|
+
import requests
|
8
|
+
from beamlit.common.settings import get_settings
|
9
|
+
from httpx import Auth, Request, Response, post
|
10
|
+
|
11
|
+
|
12
|
+
@dataclass
|
13
|
+
class DeviceLoginFinalizeResponse:
|
14
|
+
access_token: str
|
15
|
+
expires_in: int
|
16
|
+
refresh_token: str
|
17
|
+
token_type: str
|
18
|
+
|
19
|
+
|
20
|
+
class ClientCredentials(Auth):
|
21
|
+
def __init__(self, credentials, workspace_name: str, base_url: str):
|
22
|
+
self.credentials = credentials
|
23
|
+
self.workspace_name = workspace_name
|
24
|
+
self.base_url = base_url
|
25
|
+
|
26
|
+
def refresh_if_needed(self) -> Optional[Exception]:
|
27
|
+
# Need to refresh token if expires in less than 10 minutes
|
28
|
+
parts = self.credentials.access_token.split('.')
|
29
|
+
if len(parts) != 3:
|
30
|
+
return Exception("Invalid JWT token format")
|
31
|
+
try:
|
32
|
+
claims_bytes = base64.urlsafe_b64decode(parts[1] + '=' * (-len(parts[1]) % 4))
|
33
|
+
claims = json.loads(claims_bytes)
|
34
|
+
except Exception as e:
|
35
|
+
return Exception(f"Failed to decode/parse JWT claims: {str(e)}")
|
36
|
+
|
37
|
+
exp_time = time.gmtime(claims['exp'])
|
38
|
+
# Refresh if token expires in less than 10 minutes
|
39
|
+
if time.time() + (10 * 60) > time.mktime(exp_time):
|
40
|
+
return self.do_refresh()
|
41
|
+
|
42
|
+
return None
|
43
|
+
|
44
|
+
def auth_flow(self, request: Request) -> Generator[Request, Response, None]:
|
45
|
+
settings = get_settings()
|
46
|
+
if self.credentials.client_credentials and not self.credentials.refresh_token:
|
47
|
+
headers = { "Authorization": f"Basic {self.credentials.client_credentials}" }
|
48
|
+
body = { "grant_type": "client_credentials" }
|
49
|
+
response = requests.post(f"{settings.base_url}/oauth/token", headers=headers, json=body)
|
50
|
+
response.raise_for_status()
|
51
|
+
self.credentials.access_token = response.json()['access_token']
|
52
|
+
self.credentials.refresh_token = response.json()['refresh_token']
|
53
|
+
self.credentials.expires_in = response.json()['expires_in']
|
54
|
+
err = self.refresh_if_needed()
|
55
|
+
if err:
|
56
|
+
return err
|
57
|
+
|
58
|
+
request.headers['X-Beamlit-Authorization'] = f'Bearer {self.credentials.access_token}'
|
59
|
+
request.headers['X-Beamlit-Workspace'] = self.workspace_name
|
60
|
+
yield request
|
61
|
+
|
62
|
+
def do_refresh(self) -> Optional[Exception]:
|
63
|
+
if not self.credentials.refresh_token:
|
64
|
+
return Exception("No refresh token to refresh")
|
65
|
+
|
66
|
+
url = f"{self.base_url}/oauth/token"
|
67
|
+
refresh_data = {
|
68
|
+
"grant_type": "refresh_token",
|
69
|
+
"refresh_token": self.credentials.refresh_token,
|
70
|
+
"device_code": self.credentials.device_code,
|
71
|
+
"client_id": "beamlit"
|
72
|
+
}
|
73
|
+
|
74
|
+
try:
|
75
|
+
response = post(
|
76
|
+
url,
|
77
|
+
json=refresh_data,
|
78
|
+
headers={"Content-Type": "application/json"}
|
79
|
+
)
|
80
|
+
response.raise_for_status()
|
81
|
+
finalize_response = DeviceLoginFinalizeResponse(**response.json())
|
82
|
+
|
83
|
+
if not finalize_response.refresh_token:
|
84
|
+
finalize_response.refresh_token = self.credentials.refresh_token
|
85
|
+
|
86
|
+
from .credentials import Credentials, save_credentials
|
87
|
+
creds = Credentials(
|
88
|
+
access_token=finalize_response.access_token,
|
89
|
+
refresh_token=finalize_response.refresh_token,
|
90
|
+
expires_in=finalize_response.expires_in,
|
91
|
+
device_code=self.credentials.device_code
|
92
|
+
)
|
93
|
+
|
94
|
+
self.credentials = creds
|
95
|
+
save_credentials(self.workspace_name, creds)
|
96
|
+
return None
|
97
|
+
|
98
|
+
except Exception as e:
|
99
|
+
return Exception(f"Failed to refresh token: {str(e)}")
|
@@ -3,6 +3,7 @@ from pathlib import Path
|
|
3
3
|
from typing import List
|
4
4
|
|
5
5
|
import yaml
|
6
|
+
from beamlit.common.settings import Settings
|
6
7
|
|
7
8
|
|
8
9
|
@dataclass
|
@@ -12,7 +13,7 @@ class Credentials:
|
|
12
13
|
refresh_token: str = ""
|
13
14
|
expires_in: int = 0
|
14
15
|
device_code: str = ""
|
15
|
-
|
16
|
+
client_credentials: str = ""
|
16
17
|
@dataclass
|
17
18
|
class WorkspaceConfig:
|
18
19
|
name: str
|
@@ -115,6 +116,11 @@ def load_credentials(workspace_name: str) -> Credentials:
|
|
115
116
|
return workspace.credentials
|
116
117
|
return Credentials()
|
117
118
|
|
119
|
+
def load_credentials_from_config(config: Settings) -> Credentials:
|
120
|
+
return Credentials(
|
121
|
+
api_key=config.api_key,
|
122
|
+
client_credentials=config.client_credentials
|
123
|
+
)
|
118
124
|
|
119
125
|
def create_home_dir_if_missing():
|
120
126
|
home_dir = Path.home()
|
@@ -86,13 +86,11 @@ class BearerToken(Auth):
|
|
86
86
|
}
|
87
87
|
|
88
88
|
try:
|
89
|
-
print(refresh_data)
|
90
89
|
response = post(
|
91
90
|
url,
|
92
91
|
json=refresh_data,
|
93
92
|
headers={"Content-Type": "application/json"}
|
94
93
|
)
|
95
|
-
print(response.text)
|
96
94
|
response.raise_for_status()
|
97
95
|
finalize_response = DeviceLoginFinalizeResponse(**response.json())
|
98
96
|
|