beamlit 0.0.19__py3-none-any.whl → 0.0.20rc1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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.19.dist-info → beamlit-0.0.20rc1.dist-info}/METADATA +5 -2
- {beamlit-0.0.19.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.19.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
|
|