flwr-nightly 1.14.0.dev20241210__py3-none-any.whl → 1.14.0.dev20241211__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.
Potentially problematic release.
This version of flwr-nightly might be problematic. Click here for more details.
- flwr/cli/app.py +2 -0
- flwr/cli/cli_user_auth_interceptor.py +86 -0
- flwr/cli/config_utils.py +18 -2
- flwr/cli/log.py +10 -31
- flwr/cli/login/__init__.py +21 -0
- flwr/cli/login/login.py +82 -0
- flwr/cli/ls.py +10 -40
- flwr/cli/run/run.py +14 -25
- flwr/cli/stop.py +9 -39
- flwr/cli/utils.py +108 -1
- flwr/common/config.py +2 -1
- flwr/common/constant.py +4 -1
- flwr/common/secure_aggregation/secaggplus_utils.py +2 -2
- flwr/common/telemetry.py +2 -1
- flwr/proto/serverappio_pb2.py +18 -18
- flwr/proto/serverappio_pb2.pyi +8 -2
- flwr/proto/serverappio_pb2_grpc.py +34 -0
- flwr/proto/serverappio_pb2_grpc.pyi +13 -0
- flwr/server/driver/grpc_driver.py +6 -2
- flwr/server/superlink/driver/serverappio_servicer.py +18 -0
- {flwr_nightly-1.14.0.dev20241210.dist-info → flwr_nightly-1.14.0.dev20241211.dist-info}/METADATA +6 -6
- {flwr_nightly-1.14.0.dev20241210.dist-info → flwr_nightly-1.14.0.dev20241211.dist-info}/RECORD +25 -22
- {flwr_nightly-1.14.0.dev20241210.dist-info → flwr_nightly-1.14.0.dev20241211.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.14.0.dev20241210.dist-info → flwr_nightly-1.14.0.dev20241211.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.14.0.dev20241210.dist-info → flwr_nightly-1.14.0.dev20241211.dist-info}/entry_points.txt +0 -0
flwr/cli/app.py
CHANGED
|
@@ -20,6 +20,7 @@ from typer.main import get_command
|
|
|
20
20
|
from .build import build
|
|
21
21
|
from .install import install
|
|
22
22
|
from .log import log
|
|
23
|
+
from .login import login
|
|
23
24
|
from .ls import ls
|
|
24
25
|
from .new import new
|
|
25
26
|
from .run import run
|
|
@@ -41,6 +42,7 @@ app.command()(install)
|
|
|
41
42
|
app.command()(log)
|
|
42
43
|
app.command()(ls)
|
|
43
44
|
app.command()(stop)
|
|
45
|
+
app.command()(login)
|
|
44
46
|
|
|
45
47
|
typer_click_object = get_command(app)
|
|
46
48
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Copyright 2024 Flower Labs GmbH. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
# ==============================================================================
|
|
15
|
+
"""Flower run interceptor."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from typing import Any, Callable, Union
|
|
19
|
+
|
|
20
|
+
import grpc
|
|
21
|
+
|
|
22
|
+
from flwr.common.auth_plugin import CliAuthPlugin
|
|
23
|
+
from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
|
24
|
+
StartRunRequest,
|
|
25
|
+
StreamLogsRequest,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
Request = Union[
|
|
29
|
+
StartRunRequest,
|
|
30
|
+
StreamLogsRequest,
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class CliUserAuthInterceptor(
|
|
35
|
+
grpc.UnaryUnaryClientInterceptor, grpc.UnaryStreamClientInterceptor # type: ignore
|
|
36
|
+
):
|
|
37
|
+
"""CLI interceptor for user authentication."""
|
|
38
|
+
|
|
39
|
+
def __init__(self, auth_plugin: CliAuthPlugin):
|
|
40
|
+
self.auth_plugin = auth_plugin
|
|
41
|
+
|
|
42
|
+
def _authenticated_call(
|
|
43
|
+
self,
|
|
44
|
+
continuation: Callable[[Any, Any], Any],
|
|
45
|
+
client_call_details: grpc.ClientCallDetails,
|
|
46
|
+
request: Request,
|
|
47
|
+
) -> grpc.Call:
|
|
48
|
+
"""Send and receive tokens via metadata."""
|
|
49
|
+
new_metadata = self.auth_plugin.write_tokens_to_metadata(
|
|
50
|
+
client_call_details.metadata or []
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
details = client_call_details._replace(metadata=new_metadata)
|
|
54
|
+
|
|
55
|
+
response = continuation(details, request)
|
|
56
|
+
if response.initial_metadata():
|
|
57
|
+
retrieved_metadata = dict(response.initial_metadata())
|
|
58
|
+
self.auth_plugin.store_tokens(retrieved_metadata)
|
|
59
|
+
|
|
60
|
+
return response
|
|
61
|
+
|
|
62
|
+
def intercept_unary_unary(
|
|
63
|
+
self,
|
|
64
|
+
continuation: Callable[[Any, Any], Any],
|
|
65
|
+
client_call_details: grpc.ClientCallDetails,
|
|
66
|
+
request: Request,
|
|
67
|
+
) -> grpc.Call:
|
|
68
|
+
"""Intercept a unary-unary call for user authentication.
|
|
69
|
+
|
|
70
|
+
This method intercepts a unary-unary RPC call initiated from the CLI and adds
|
|
71
|
+
the required authentication tokens to the RPC metadata.
|
|
72
|
+
"""
|
|
73
|
+
return self._authenticated_call(continuation, client_call_details, request)
|
|
74
|
+
|
|
75
|
+
def intercept_unary_stream(
|
|
76
|
+
self,
|
|
77
|
+
continuation: Callable[[Any, Any], Any],
|
|
78
|
+
client_call_details: grpc.ClientCallDetails,
|
|
79
|
+
request: Request,
|
|
80
|
+
) -> grpc.Call:
|
|
81
|
+
"""Intercept a unary-stream call for user authentication.
|
|
82
|
+
|
|
83
|
+
This method intercepts a unary-stream RPC call initiated from the CLI and adds
|
|
84
|
+
the required authentication tokens to the RPC metadata.
|
|
85
|
+
"""
|
|
86
|
+
return self._authenticated_call(continuation, client_call_details, request)
|
flwr/cli/config_utils.py
CHANGED
|
@@ -230,10 +230,14 @@ def load_from_string(toml_content: str) -> Optional[dict[str, Any]]:
|
|
|
230
230
|
return None
|
|
231
231
|
|
|
232
232
|
|
|
233
|
-
def
|
|
233
|
+
def process_loaded_project_config(
|
|
234
234
|
config: Union[dict[str, Any], None], errors: list[str], warnings: list[str]
|
|
235
235
|
) -> dict[str, Any]:
|
|
236
|
-
"""
|
|
236
|
+
"""Process and return the loaded project configuration.
|
|
237
|
+
|
|
238
|
+
This function handles errors and warnings from the `load_and_validate` function,
|
|
239
|
+
exits on critical issues, and returns the validated configuration.
|
|
240
|
+
"""
|
|
237
241
|
if config is None:
|
|
238
242
|
typer.secho(
|
|
239
243
|
"Project configuration could not be loaded.\n"
|
|
@@ -324,3 +328,15 @@ def validate_certificate_in_federation_config(
|
|
|
324
328
|
raise typer.Exit(code=1)
|
|
325
329
|
|
|
326
330
|
return insecure, root_certificates_bytes
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def exit_if_no_address(federation_config: dict[str, Any], cmd: str) -> None:
|
|
334
|
+
"""Exit if the provided federation_config has no "address" key."""
|
|
335
|
+
if "address" not in federation_config:
|
|
336
|
+
typer.secho(
|
|
337
|
+
f"❌ `flwr {cmd}` currently works with a SuperLink. Ensure that the correct"
|
|
338
|
+
"SuperLink (Exec API) address is provided in `pyproject.toml`.",
|
|
339
|
+
fg=typer.colors.RED,
|
|
340
|
+
bold=True,
|
|
341
|
+
)
|
|
342
|
+
raise typer.Exit(code=1)
|
flwr/cli/log.py
CHANGED
|
@@ -23,17 +23,18 @@ import grpc
|
|
|
23
23
|
import typer
|
|
24
24
|
|
|
25
25
|
from flwr.cli.config_utils import (
|
|
26
|
+
exit_if_no_address,
|
|
26
27
|
load_and_validate,
|
|
27
|
-
|
|
28
|
+
process_loaded_project_config,
|
|
28
29
|
validate_federation_in_project_config,
|
|
29
|
-
validate_project_config,
|
|
30
30
|
)
|
|
31
31
|
from flwr.common.constant import CONN_RECONNECT_INTERVAL, CONN_REFRESH_PERIOD
|
|
32
|
-
from flwr.common.grpc import GRPC_MAX_MESSAGE_LENGTH, create_channel
|
|
33
32
|
from flwr.common.logger import log as logger
|
|
34
33
|
from flwr.proto.exec_pb2 import StreamLogsRequest # pylint: disable=E0611
|
|
35
34
|
from flwr.proto.exec_pb2_grpc import ExecStub
|
|
36
35
|
|
|
36
|
+
from .utils import init_channel, try_obtain_cli_auth_plugin
|
|
37
|
+
|
|
37
38
|
|
|
38
39
|
def start_stream(
|
|
39
40
|
run_id: int, channel: grpc.Channel, refresh_period: int = CONN_REFRESH_PERIOD
|
|
@@ -126,11 +127,6 @@ def print_logs(run_id: int, channel: grpc.Channel, timeout: int) -> None:
|
|
|
126
127
|
logger(DEBUG, "Channel closed")
|
|
127
128
|
|
|
128
129
|
|
|
129
|
-
def on_channel_state_change(channel_connectivity: str) -> None:
|
|
130
|
-
"""Log channel connectivity."""
|
|
131
|
-
logger(DEBUG, channel_connectivity)
|
|
132
|
-
|
|
133
|
-
|
|
134
130
|
def log(
|
|
135
131
|
run_id: Annotated[
|
|
136
132
|
int,
|
|
@@ -157,41 +153,24 @@ def log(
|
|
|
157
153
|
|
|
158
154
|
pyproject_path = app / "pyproject.toml" if app else None
|
|
159
155
|
config, errors, warnings = load_and_validate(path=pyproject_path)
|
|
160
|
-
config =
|
|
156
|
+
config = process_loaded_project_config(config, errors, warnings)
|
|
161
157
|
federation, federation_config = validate_federation_in_project_config(
|
|
162
158
|
federation, config
|
|
163
159
|
)
|
|
160
|
+
exit_if_no_address(federation_config, "log")
|
|
164
161
|
|
|
165
|
-
|
|
166
|
-
typer.secho(
|
|
167
|
-
"❌ `flwr log` currently works with Exec API. Ensure that the correct"
|
|
168
|
-
"Exec API address is provided in the `pyproject.toml`.",
|
|
169
|
-
fg=typer.colors.RED,
|
|
170
|
-
bold=True,
|
|
171
|
-
)
|
|
172
|
-
raise typer.Exit(code=1)
|
|
173
|
-
|
|
174
|
-
_log_with_exec_api(app, federation_config, run_id, stream)
|
|
162
|
+
_log_with_exec_api(app, federation, federation_config, run_id, stream)
|
|
175
163
|
|
|
176
164
|
|
|
177
165
|
def _log_with_exec_api(
|
|
178
166
|
app: Path,
|
|
167
|
+
federation: str,
|
|
179
168
|
federation_config: dict[str, Any],
|
|
180
169
|
run_id: int,
|
|
181
170
|
stream: bool,
|
|
182
171
|
) -> None:
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
app, federation_config
|
|
186
|
-
)
|
|
187
|
-
channel = create_channel(
|
|
188
|
-
server_address=federation_config["address"],
|
|
189
|
-
insecure=insecure,
|
|
190
|
-
root_certificates=root_certificates_bytes,
|
|
191
|
-
max_message_length=GRPC_MAX_MESSAGE_LENGTH,
|
|
192
|
-
interceptors=None,
|
|
193
|
-
)
|
|
194
|
-
channel.subscribe(on_channel_state_change)
|
|
172
|
+
auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
|
|
173
|
+
channel = init_channel(app, federation_config, auth_plugin)
|
|
195
174
|
|
|
196
175
|
if stream:
|
|
197
176
|
start_stream(run_id, channel, CONN_REFRESH_PERIOD)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Copyright 2024 Flower Labs GmbH. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
# ==============================================================================
|
|
15
|
+
"""Flower command line interface `login` command."""
|
|
16
|
+
|
|
17
|
+
from .login import login as login
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"login",
|
|
21
|
+
]
|
flwr/cli/login/login.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Copyright 2024 Flower Labs GmbH. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
# ==============================================================================
|
|
15
|
+
"""Flower command line interface `login` command."""
|
|
16
|
+
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Annotated, Optional
|
|
19
|
+
|
|
20
|
+
import typer
|
|
21
|
+
|
|
22
|
+
from flwr.cli.config_utils import (
|
|
23
|
+
exit_if_no_address,
|
|
24
|
+
load_and_validate,
|
|
25
|
+
process_loaded_project_config,
|
|
26
|
+
validate_federation_in_project_config,
|
|
27
|
+
)
|
|
28
|
+
from flwr.common.constant import AUTH_TYPE
|
|
29
|
+
from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
|
30
|
+
GetLoginDetailsRequest,
|
|
31
|
+
GetLoginDetailsResponse,
|
|
32
|
+
)
|
|
33
|
+
from flwr.proto.exec_pb2_grpc import ExecStub
|
|
34
|
+
|
|
35
|
+
from ..utils import init_channel, try_obtain_cli_auth_plugin
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def login( # pylint: disable=R0914
|
|
39
|
+
app: Annotated[
|
|
40
|
+
Path,
|
|
41
|
+
typer.Argument(help="Path of the Flower App to run."),
|
|
42
|
+
] = Path("."),
|
|
43
|
+
federation: Annotated[
|
|
44
|
+
Optional[str],
|
|
45
|
+
typer.Argument(help="Name of the federation to login into."),
|
|
46
|
+
] = None,
|
|
47
|
+
) -> None:
|
|
48
|
+
"""Login to Flower SuperLink."""
|
|
49
|
+
typer.secho("Loading project configuration... ", fg=typer.colors.BLUE)
|
|
50
|
+
|
|
51
|
+
pyproject_path = app / "pyproject.toml" if app else None
|
|
52
|
+
config, errors, warnings = load_and_validate(path=pyproject_path)
|
|
53
|
+
|
|
54
|
+
config = process_loaded_project_config(config, errors, warnings)
|
|
55
|
+
federation, federation_config = validate_federation_in_project_config(
|
|
56
|
+
federation, config
|
|
57
|
+
)
|
|
58
|
+
exit_if_no_address(federation_config, "login")
|
|
59
|
+
channel = init_channel(app, federation_config, None)
|
|
60
|
+
stub = ExecStub(channel)
|
|
61
|
+
|
|
62
|
+
login_request = GetLoginDetailsRequest()
|
|
63
|
+
login_response: GetLoginDetailsResponse = stub.GetLoginDetails(login_request)
|
|
64
|
+
|
|
65
|
+
# Get the auth plugin
|
|
66
|
+
auth_type = login_response.login_details.get(AUTH_TYPE)
|
|
67
|
+
auth_plugin = try_obtain_cli_auth_plugin(
|
|
68
|
+
app, federation, federation_config, auth_type
|
|
69
|
+
)
|
|
70
|
+
if auth_plugin is None:
|
|
71
|
+
typer.secho(
|
|
72
|
+
f'❌ Authentication type "{auth_type}" not found',
|
|
73
|
+
fg=typer.colors.RED,
|
|
74
|
+
bold=True,
|
|
75
|
+
)
|
|
76
|
+
raise typer.Exit(code=1)
|
|
77
|
+
|
|
78
|
+
# Login
|
|
79
|
+
auth_config = auth_plugin.login(dict(login_response.login_details), stub)
|
|
80
|
+
|
|
81
|
+
# Store the tokens
|
|
82
|
+
auth_plugin.store_tokens(auth_config)
|
flwr/cli/ls.py
CHANGED
|
@@ -18,11 +18,9 @@
|
|
|
18
18
|
import io
|
|
19
19
|
import json
|
|
20
20
|
from datetime import datetime, timedelta
|
|
21
|
-
from logging import DEBUG
|
|
22
21
|
from pathlib import Path
|
|
23
|
-
from typing import Annotated,
|
|
22
|
+
from typing import Annotated, Optional, Union
|
|
24
23
|
|
|
25
|
-
import grpc
|
|
26
24
|
import typer
|
|
27
25
|
from rich.console import Console
|
|
28
26
|
from rich.table import Table
|
|
@@ -30,15 +28,14 @@ from rich.text import Text
|
|
|
30
28
|
from typer import Exit
|
|
31
29
|
|
|
32
30
|
from flwr.cli.config_utils import (
|
|
31
|
+
exit_if_no_address,
|
|
33
32
|
load_and_validate,
|
|
34
|
-
|
|
33
|
+
process_loaded_project_config,
|
|
35
34
|
validate_federation_in_project_config,
|
|
36
|
-
validate_project_config,
|
|
37
35
|
)
|
|
38
36
|
from flwr.common.constant import FAB_CONFIG_FILE, CliOutputFormat, SubStatus
|
|
39
37
|
from flwr.common.date import format_timedelta, isoformat8601_utc
|
|
40
|
-
from flwr.common.
|
|
41
|
-
from flwr.common.logger import log, redirect_output, remove_emojis, restore_output
|
|
38
|
+
from flwr.common.logger import redirect_output, remove_emojis, restore_output
|
|
42
39
|
from flwr.common.serde import run_from_proto
|
|
43
40
|
from flwr.common.typing import Run
|
|
44
41
|
from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
|
@@ -47,6 +44,8 @@ from flwr.proto.exec_pb2 import ( # pylint: disable=E0611
|
|
|
47
44
|
)
|
|
48
45
|
from flwr.proto.exec_pb2_grpc import ExecStub
|
|
49
46
|
|
|
47
|
+
from .utils import init_channel, try_obtain_cli_auth_plugin
|
|
48
|
+
|
|
50
49
|
_RunListType = tuple[int, str, str, str, str, str, str, str, str]
|
|
51
50
|
|
|
52
51
|
|
|
@@ -94,27 +93,19 @@ def ls( # pylint: disable=too-many-locals, too-many-branches
|
|
|
94
93
|
|
|
95
94
|
pyproject_path = app / FAB_CONFIG_FILE if app else None
|
|
96
95
|
config, errors, warnings = load_and_validate(path=pyproject_path)
|
|
97
|
-
config =
|
|
96
|
+
config = process_loaded_project_config(config, errors, warnings)
|
|
98
97
|
federation, federation_config = validate_federation_in_project_config(
|
|
99
98
|
federation, config
|
|
100
99
|
)
|
|
101
|
-
|
|
102
|
-
if "address" not in federation_config:
|
|
103
|
-
typer.secho(
|
|
104
|
-
"❌ `flwr ls` currently works with Exec API. Ensure that the correct"
|
|
105
|
-
"Exec API address is provided in the `pyproject.toml`.",
|
|
106
|
-
fg=typer.colors.RED,
|
|
107
|
-
bold=True,
|
|
108
|
-
)
|
|
109
|
-
raise typer.Exit(code=1)
|
|
100
|
+
exit_if_no_address(federation_config, "ls")
|
|
110
101
|
|
|
111
102
|
try:
|
|
112
103
|
if runs and run_id is not None:
|
|
113
104
|
raise ValueError(
|
|
114
105
|
"The options '--runs' and '--run-id' are mutually exclusive."
|
|
115
106
|
)
|
|
116
|
-
|
|
117
|
-
channel =
|
|
107
|
+
auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
|
|
108
|
+
channel = init_channel(app, federation_config, auth_plugin)
|
|
118
109
|
stub = ExecStub(channel)
|
|
119
110
|
|
|
120
111
|
# Display information about a specific run ID
|
|
@@ -154,27 +145,6 @@ def ls( # pylint: disable=too-many-locals, too-many-branches
|
|
|
154
145
|
captured_output.close()
|
|
155
146
|
|
|
156
147
|
|
|
157
|
-
def on_channel_state_change(channel_connectivity: str) -> None:
|
|
158
|
-
"""Log channel connectivity."""
|
|
159
|
-
log(DEBUG, channel_connectivity)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def _init_channel(app: Path, federation_config: dict[str, Any]) -> grpc.Channel:
|
|
163
|
-
"""Initialize gRPC channel to the Exec API."""
|
|
164
|
-
insecure, root_certificates_bytes = validate_certificate_in_federation_config(
|
|
165
|
-
app, federation_config
|
|
166
|
-
)
|
|
167
|
-
channel = create_channel(
|
|
168
|
-
server_address=federation_config["address"],
|
|
169
|
-
insecure=insecure,
|
|
170
|
-
root_certificates=root_certificates_bytes,
|
|
171
|
-
max_message_length=GRPC_MAX_MESSAGE_LENGTH,
|
|
172
|
-
interceptors=None,
|
|
173
|
-
)
|
|
174
|
-
channel.subscribe(on_channel_state_change)
|
|
175
|
-
return channel
|
|
176
|
-
|
|
177
|
-
|
|
178
148
|
def _format_runs(run_dict: dict[int, Run], now_isoformat: str) -> list[_RunListType]:
|
|
179
149
|
"""Format runs to a list."""
|
|
180
150
|
|
flwr/cli/run/run.py
CHANGED
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
import io
|
|
18
18
|
import json
|
|
19
19
|
import subprocess
|
|
20
|
-
from logging import DEBUG
|
|
21
20
|
from pathlib import Path
|
|
22
21
|
from typing import Annotated, Any, Optional, Union
|
|
23
22
|
|
|
@@ -28,9 +27,8 @@ from flwr.cli.build import build
|
|
|
28
27
|
from flwr.cli.config_utils import (
|
|
29
28
|
get_fab_metadata,
|
|
30
29
|
load_and_validate,
|
|
31
|
-
|
|
30
|
+
process_loaded_project_config,
|
|
32
31
|
validate_federation_in_project_config,
|
|
33
|
-
validate_project_config,
|
|
34
32
|
)
|
|
35
33
|
from flwr.common.config import (
|
|
36
34
|
flatten_dict,
|
|
@@ -38,8 +36,7 @@ from flwr.common.config import (
|
|
|
38
36
|
user_config_to_configsrecord,
|
|
39
37
|
)
|
|
40
38
|
from flwr.common.constant import CliOutputFormat
|
|
41
|
-
from flwr.common.
|
|
42
|
-
from flwr.common.logger import log, redirect_output, remove_emojis, restore_output
|
|
39
|
+
from flwr.common.logger import redirect_output, remove_emojis, restore_output
|
|
43
40
|
from flwr.common.serde import (
|
|
44
41
|
configs_record_to_proto,
|
|
45
42
|
fab_to_proto,
|
|
@@ -50,15 +47,11 @@ from flwr.proto.exec_pb2 import StartRunRequest # pylint: disable=E0611
|
|
|
50
47
|
from flwr.proto.exec_pb2_grpc import ExecStub
|
|
51
48
|
|
|
52
49
|
from ..log import start_stream
|
|
50
|
+
from ..utils import init_channel, try_obtain_cli_auth_plugin
|
|
53
51
|
|
|
54
52
|
CONN_REFRESH_PERIOD = 60 # Connection refresh period for log streaming (seconds)
|
|
55
53
|
|
|
56
54
|
|
|
57
|
-
def on_channel_state_change(channel_connectivity: str) -> None:
|
|
58
|
-
"""Log channel connectivity."""
|
|
59
|
-
log(DEBUG, channel_connectivity)
|
|
60
|
-
|
|
61
|
-
|
|
62
55
|
# pylint: disable-next=too-many-locals
|
|
63
56
|
def run(
|
|
64
57
|
app: Annotated[
|
|
@@ -108,14 +101,19 @@ def run(
|
|
|
108
101
|
|
|
109
102
|
pyproject_path = app / "pyproject.toml" if app else None
|
|
110
103
|
config, errors, warnings = load_and_validate(path=pyproject_path)
|
|
111
|
-
config =
|
|
104
|
+
config = process_loaded_project_config(config, errors, warnings)
|
|
112
105
|
federation, federation_config = validate_federation_in_project_config(
|
|
113
106
|
federation, config
|
|
114
107
|
)
|
|
115
108
|
|
|
116
109
|
if "address" in federation_config:
|
|
117
110
|
_run_with_exec_api(
|
|
118
|
-
app,
|
|
111
|
+
app,
|
|
112
|
+
federation,
|
|
113
|
+
federation_config,
|
|
114
|
+
config_overrides,
|
|
115
|
+
stream,
|
|
116
|
+
output_format,
|
|
119
117
|
)
|
|
120
118
|
else:
|
|
121
119
|
_run_without_exec_api(app, federation_config, config_overrides, federation)
|
|
@@ -136,26 +134,17 @@ def run(
|
|
|
136
134
|
captured_output.close()
|
|
137
135
|
|
|
138
136
|
|
|
139
|
-
# pylint: disable-next=
|
|
137
|
+
# pylint: disable-next=R0913, R0914, R0917
|
|
140
138
|
def _run_with_exec_api(
|
|
141
139
|
app: Path,
|
|
140
|
+
federation: str,
|
|
142
141
|
federation_config: dict[str, Any],
|
|
143
142
|
config_overrides: Optional[list[str]],
|
|
144
143
|
stream: bool,
|
|
145
144
|
output_format: str,
|
|
146
145
|
) -> None:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
app, federation_config
|
|
150
|
-
)
|
|
151
|
-
channel = create_channel(
|
|
152
|
-
server_address=federation_config["address"],
|
|
153
|
-
insecure=insecure,
|
|
154
|
-
root_certificates=root_certificates_bytes,
|
|
155
|
-
max_message_length=GRPC_MAX_MESSAGE_LENGTH,
|
|
156
|
-
interceptors=None,
|
|
157
|
-
)
|
|
158
|
-
channel.subscribe(on_channel_state_change)
|
|
146
|
+
auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
|
|
147
|
+
channel = init_channel(app, federation_config, auth_plugin)
|
|
159
148
|
stub = ExecStub(channel)
|
|
160
149
|
|
|
161
150
|
fab_path, fab_hash = build(app)
|
flwr/cli/stop.py
CHANGED
|
@@ -15,25 +15,23 @@
|
|
|
15
15
|
"""Flower command line interface `stop` command."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
from logging import DEBUG
|
|
19
18
|
from pathlib import Path
|
|
20
|
-
from typing import Annotated,
|
|
19
|
+
from typing import Annotated, Optional
|
|
21
20
|
|
|
22
|
-
import grpc
|
|
23
21
|
import typer
|
|
24
22
|
|
|
25
23
|
from flwr.cli.config_utils import (
|
|
24
|
+
exit_if_no_address,
|
|
26
25
|
load_and_validate,
|
|
27
|
-
|
|
26
|
+
process_loaded_project_config,
|
|
28
27
|
validate_federation_in_project_config,
|
|
29
|
-
validate_project_config,
|
|
30
28
|
)
|
|
31
29
|
from flwr.common.constant import FAB_CONFIG_FILE
|
|
32
|
-
from flwr.common.grpc import GRPC_MAX_MESSAGE_LENGTH, create_channel
|
|
33
|
-
from flwr.common.logger import log
|
|
34
30
|
from flwr.proto.exec_pb2 import StopRunRequest, StopRunResponse # pylint: disable=E0611
|
|
35
31
|
from flwr.proto.exec_pb2_grpc import ExecStub
|
|
36
32
|
|
|
33
|
+
from .utils import init_channel, try_obtain_cli_auth_plugin
|
|
34
|
+
|
|
37
35
|
|
|
38
36
|
def stop(
|
|
39
37
|
run_id: Annotated[ # pylint: disable=unused-argument
|
|
@@ -55,22 +53,15 @@ def stop(
|
|
|
55
53
|
|
|
56
54
|
pyproject_path = app / FAB_CONFIG_FILE if app else None
|
|
57
55
|
config, errors, warnings = load_and_validate(path=pyproject_path)
|
|
58
|
-
config =
|
|
56
|
+
config = process_loaded_project_config(config, errors, warnings)
|
|
59
57
|
federation, federation_config = validate_federation_in_project_config(
|
|
60
58
|
federation, config
|
|
61
59
|
)
|
|
62
|
-
|
|
63
|
-
if "address" not in federation_config:
|
|
64
|
-
typer.secho(
|
|
65
|
-
"❌ `flwr stop` currently works with Exec API. Ensure that the correct"
|
|
66
|
-
"Exec API address is provided in the `pyproject.toml`.",
|
|
67
|
-
fg=typer.colors.RED,
|
|
68
|
-
bold=True,
|
|
69
|
-
)
|
|
70
|
-
raise typer.Exit(code=1)
|
|
60
|
+
exit_if_no_address(federation_config, "stop")
|
|
71
61
|
|
|
72
62
|
try:
|
|
73
|
-
|
|
63
|
+
auth_plugin = try_obtain_cli_auth_plugin(app, federation, federation_config)
|
|
64
|
+
channel = init_channel(app, federation_config, auth_plugin)
|
|
74
65
|
stub = ExecStub(channel) # pylint: disable=unused-variable # noqa: F841
|
|
75
66
|
|
|
76
67
|
typer.secho(f"✋ Stopping run ID {run_id}...", fg=typer.colors.GREEN)
|
|
@@ -87,27 +78,6 @@ def stop(
|
|
|
87
78
|
channel.close()
|
|
88
79
|
|
|
89
80
|
|
|
90
|
-
def on_channel_state_change(channel_connectivity: str) -> None:
|
|
91
|
-
"""Log channel connectivity."""
|
|
92
|
-
log(DEBUG, channel_connectivity)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def _init_channel(app: Path, federation_config: dict[str, Any]) -> grpc.Channel:
|
|
96
|
-
"""Initialize gRPC channel to the Exec API."""
|
|
97
|
-
insecure, root_certificates_bytes = validate_certificate_in_federation_config(
|
|
98
|
-
app, federation_config
|
|
99
|
-
)
|
|
100
|
-
channel = create_channel(
|
|
101
|
-
server_address=federation_config["address"],
|
|
102
|
-
insecure=insecure,
|
|
103
|
-
root_certificates=root_certificates_bytes,
|
|
104
|
-
max_message_length=GRPC_MAX_MESSAGE_LENGTH,
|
|
105
|
-
interceptors=None,
|
|
106
|
-
)
|
|
107
|
-
channel.subscribe(on_channel_state_change)
|
|
108
|
-
return channel
|
|
109
|
-
|
|
110
|
-
|
|
111
81
|
def _stop_run(
|
|
112
82
|
stub: ExecStub, # pylint: disable=unused-argument
|
|
113
83
|
run_id: int, # pylint: disable=unused-argument
|
flwr/cli/utils.py
CHANGED
|
@@ -15,12 +15,32 @@
|
|
|
15
15
|
"""Flower command line interface utils."""
|
|
16
16
|
|
|
17
17
|
import hashlib
|
|
18
|
+
import json
|
|
18
19
|
import re
|
|
20
|
+
from logging import DEBUG
|
|
19
21
|
from pathlib import Path
|
|
20
|
-
from typing import Callable, Optional, cast
|
|
22
|
+
from typing import Any, Callable, Optional, cast
|
|
21
23
|
|
|
24
|
+
import grpc
|
|
22
25
|
import typer
|
|
23
26
|
|
|
27
|
+
from flwr.cli.cli_user_auth_interceptor import CliUserAuthInterceptor
|
|
28
|
+
from flwr.common.address import parse_address
|
|
29
|
+
from flwr.common.auth_plugin import CliAuthPlugin
|
|
30
|
+
from flwr.common.constant import AUTH_TYPE, CREDENTIALS_DIR, FLWR_DIR
|
|
31
|
+
from flwr.common.grpc import GRPC_MAX_MESSAGE_LENGTH, create_channel
|
|
32
|
+
from flwr.common.logger import log
|
|
33
|
+
|
|
34
|
+
from .config_utils import validate_certificate_in_federation_config
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
from flwr.ee import get_cli_auth_plugins
|
|
38
|
+
except ImportError:
|
|
39
|
+
|
|
40
|
+
def get_cli_auth_plugins() -> dict[str, type[CliAuthPlugin]]:
|
|
41
|
+
"""Return all CLI authentication plugins."""
|
|
42
|
+
raise NotImplementedError("No authentication plugins are currently supported.")
|
|
43
|
+
|
|
24
44
|
|
|
25
45
|
def prompt_text(
|
|
26
46
|
text: str,
|
|
@@ -136,3 +156,90 @@ def get_sha256_hash(file_path: Path) -> str:
|
|
|
136
156
|
break
|
|
137
157
|
sha256.update(data)
|
|
138
158
|
return sha256.hexdigest()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def get_user_auth_config_path(
|
|
162
|
+
root_dir: Path, federation: str, server_address: str
|
|
163
|
+
) -> Path:
|
|
164
|
+
"""Return the path to the user auth config file."""
|
|
165
|
+
# Parse the server address
|
|
166
|
+
parsed_addr = parse_address(server_address)
|
|
167
|
+
if parsed_addr is None:
|
|
168
|
+
raise ValueError(f"Invalid server address: {server_address}")
|
|
169
|
+
host, port, is_v6 = parsed_addr
|
|
170
|
+
formatted_addr = f"[{host}]_{port}" if is_v6 else f"{host}_{port}"
|
|
171
|
+
|
|
172
|
+
# Locate the credentials directory
|
|
173
|
+
credentials_dir = root_dir.absolute() / FLWR_DIR / CREDENTIALS_DIR
|
|
174
|
+
credentials_dir.mkdir(parents=True, exist_ok=True)
|
|
175
|
+
return credentials_dir / f"{federation}_{formatted_addr}.json"
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def try_obtain_cli_auth_plugin(
|
|
179
|
+
root_dir: Path,
|
|
180
|
+
federation: str,
|
|
181
|
+
federation_config: dict[str, Any],
|
|
182
|
+
auth_type: Optional[str] = None,
|
|
183
|
+
) -> Optional[CliAuthPlugin]:
|
|
184
|
+
"""Load the CLI-side user auth plugin for the given auth type."""
|
|
185
|
+
config_path = get_user_auth_config_path(
|
|
186
|
+
root_dir, federation, federation_config["address"]
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Load the config file if it exists
|
|
190
|
+
config: dict[str, Any] = {}
|
|
191
|
+
if config_path.exists():
|
|
192
|
+
with config_path.open("r", encoding="utf-8") as file:
|
|
193
|
+
config = json.load(file)
|
|
194
|
+
# This is the case when the user auth is not enabled
|
|
195
|
+
elif auth_type is None:
|
|
196
|
+
return None
|
|
197
|
+
|
|
198
|
+
# Get the auth type from the config if not provided
|
|
199
|
+
if auth_type is None:
|
|
200
|
+
if AUTH_TYPE not in config:
|
|
201
|
+
return None
|
|
202
|
+
auth_type = config[AUTH_TYPE]
|
|
203
|
+
|
|
204
|
+
# Retrieve auth plugin class and instantiate it
|
|
205
|
+
try:
|
|
206
|
+
all_plugins: dict[str, type[CliAuthPlugin]] = get_cli_auth_plugins()
|
|
207
|
+
auth_plugin_class = all_plugins[auth_type]
|
|
208
|
+
return auth_plugin_class(config_path)
|
|
209
|
+
except KeyError:
|
|
210
|
+
typer.echo(f"❌ Unknown user authentication type: {auth_type}")
|
|
211
|
+
raise typer.Exit(code=1) from None
|
|
212
|
+
except ImportError:
|
|
213
|
+
typer.echo("❌ No authentication plugins are currently supported.")
|
|
214
|
+
raise typer.Exit(code=1) from None
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def init_channel(
|
|
218
|
+
app: Path, federation_config: dict[str, Any], auth_plugin: Optional[CliAuthPlugin]
|
|
219
|
+
) -> grpc.Channel:
|
|
220
|
+
"""Initialize gRPC channel to the Exec API."""
|
|
221
|
+
|
|
222
|
+
def on_channel_state_change(channel_connectivity: str) -> None:
|
|
223
|
+
"""Log channel connectivity."""
|
|
224
|
+
log(DEBUG, channel_connectivity)
|
|
225
|
+
|
|
226
|
+
insecure, root_certificates_bytes = validate_certificate_in_federation_config(
|
|
227
|
+
app, federation_config
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
# Initialize the CLI-side user auth interceptor
|
|
231
|
+
interceptors: list[grpc.UnaryUnaryClientInterceptor] = []
|
|
232
|
+
if auth_plugin is not None:
|
|
233
|
+
auth_plugin.load_tokens()
|
|
234
|
+
interceptors = CliUserAuthInterceptor(auth_plugin)
|
|
235
|
+
|
|
236
|
+
# Create the gRPC channel
|
|
237
|
+
channel = create_channel(
|
|
238
|
+
server_address=federation_config["address"],
|
|
239
|
+
insecure=insecure,
|
|
240
|
+
root_certificates=root_certificates_bytes,
|
|
241
|
+
max_message_length=GRPC_MAX_MESSAGE_LENGTH,
|
|
242
|
+
interceptors=interceptors or None,
|
|
243
|
+
)
|
|
244
|
+
channel.subscribe(on_channel_state_change)
|
|
245
|
+
return channel
|
flwr/common/config.py
CHANGED
|
@@ -27,6 +27,7 @@ from flwr.common.constant import (
|
|
|
27
27
|
APP_DIR,
|
|
28
28
|
FAB_CONFIG_FILE,
|
|
29
29
|
FAB_HASH_TRUNCATION,
|
|
30
|
+
FLWR_DIR,
|
|
30
31
|
FLWR_HOME,
|
|
31
32
|
)
|
|
32
33
|
from flwr.common.typing import Run, UserConfig, UserConfigValue
|
|
@@ -38,7 +39,7 @@ def get_flwr_dir(provided_path: Optional[str] = None) -> Path:
|
|
|
38
39
|
return Path(
|
|
39
40
|
os.getenv(
|
|
40
41
|
FLWR_HOME,
|
|
41
|
-
Path(f"{os.getenv('XDG_DATA_HOME', os.getenv('HOME'))}") /
|
|
42
|
+
Path(f"{os.getenv('XDG_DATA_HOME', os.getenv('HOME'))}") / FLWR_DIR,
|
|
42
43
|
)
|
|
43
44
|
)
|
|
44
45
|
return Path(provided_path).absolute()
|
flwr/common/constant.py
CHANGED
|
@@ -80,7 +80,8 @@ FAB_ALLOWED_EXTENSIONS = {".py", ".toml", ".md"}
|
|
|
80
80
|
FAB_CONFIG_FILE = "pyproject.toml"
|
|
81
81
|
FAB_DATE = (2024, 10, 1, 0, 0, 0)
|
|
82
82
|
FAB_HASH_TRUNCATION = 8
|
|
83
|
-
|
|
83
|
+
FLWR_DIR = ".flwr" # The default Flower directory: ~/.flwr/
|
|
84
|
+
FLWR_HOME = "FLWR_HOME" # If set, override the default Flower directory
|
|
84
85
|
|
|
85
86
|
# Constants entries in Node config for Simulation
|
|
86
87
|
PARTITION_ID_KEY = "partition-id"
|
|
@@ -110,6 +111,8 @@ LOG_UPLOAD_INTERVAL = 0.2 # Minimum interval between two log uploads
|
|
|
110
111
|
# Retry configurations
|
|
111
112
|
MAX_RETRY_DELAY = 20 # Maximum delay duration between two consecutive retries.
|
|
112
113
|
|
|
114
|
+
# Constants for user authentication
|
|
115
|
+
CREDENTIALS_DIR = ".credentials"
|
|
113
116
|
AUTH_TYPE = "auth_type"
|
|
114
117
|
|
|
115
118
|
|
|
@@ -93,8 +93,8 @@ def pseudo_rand_gen(
|
|
|
93
93
|
output = []
|
|
94
94
|
for dimension in dimensions_list:
|
|
95
95
|
if len(dimension) == 0:
|
|
96
|
-
arr = np.array(gen.randint(0, num_range - 1), dtype=
|
|
96
|
+
arr = np.array(gen.randint(0, num_range - 1), dtype=np.int64)
|
|
97
97
|
else:
|
|
98
|
-
arr = gen.randint(0, num_range - 1, dimension)
|
|
98
|
+
arr = gen.randint(0, num_range - 1, dimension, dtype=np.int64)
|
|
99
99
|
output.append(arr)
|
|
100
100
|
return output
|
flwr/common/telemetry.py
CHANGED
|
@@ -27,6 +27,7 @@ from enum import Enum, auto
|
|
|
27
27
|
from pathlib import Path
|
|
28
28
|
from typing import Any, Optional, Union, cast
|
|
29
29
|
|
|
30
|
+
from flwr.common.constant import FLWR_DIR
|
|
30
31
|
from flwr.common.version import package_name, package_version
|
|
31
32
|
|
|
32
33
|
FLWR_TELEMETRY_ENABLED = os.getenv("FLWR_TELEMETRY_ENABLED", "1")
|
|
@@ -86,7 +87,7 @@ def _get_source_id() -> str:
|
|
|
86
87
|
# If the home directory can’t be resolved, RuntimeError is raised.
|
|
87
88
|
return source_id
|
|
88
89
|
|
|
89
|
-
flwr_dir = home.joinpath(
|
|
90
|
+
flwr_dir = home.joinpath(FLWR_DIR)
|
|
90
91
|
# Create .flwr directory if it does not exist yet.
|
|
91
92
|
try:
|
|
92
93
|
flwr_dir.mkdir(parents=True, exist_ok=True)
|
flwr/proto/serverappio_pb2.py
CHANGED
|
@@ -20,7 +20,7 @@ from flwr.proto import run_pb2 as flwr_dot_proto_dot_run__pb2
|
|
|
20
20
|
from flwr.proto import fab_pb2 as flwr_dot_proto_dot_fab__pb2
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x66lwr/proto/serverappio.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/log.proto\x1a\x15\x66lwr/proto/node.proto\x1a\x18\x66lwr/proto/message.proto\x1a\x15\x66lwr/proto/task.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x14\x66lwr/proto/fab.proto\"!\n\x0fGetNodesRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"3\n\x10GetNodesResponse\x12\x1f\n\x05nodes\x18\x01 \x03(\x0b\x32\x10.flwr.proto.Node\"
|
|
23
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x66lwr/proto/serverappio.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/log.proto\x1a\x15\x66lwr/proto/node.proto\x1a\x18\x66lwr/proto/message.proto\x1a\x15\x66lwr/proto/task.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x14\x66lwr/proto/fab.proto\"!\n\x0fGetNodesRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\"3\n\x10GetNodesResponse\x12\x1f\n\x05nodes\x18\x01 \x03(\x0b\x32\x10.flwr.proto.Node\"P\n\x12PushTaskInsRequest\x12*\n\rtask_ins_list\x18\x01 \x03(\x0b\x32\x13.flwr.proto.TaskIns\x12\x0e\n\x06run_id\x18\x02 \x01(\x04\"\'\n\x13PushTaskInsResponse\x12\x10\n\x08task_ids\x18\x02 \x03(\t\"V\n\x12PullTaskResRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x10\n\x08task_ids\x18\x02 \x03(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\x04\"A\n\x13PullTaskResResponse\x12*\n\rtask_res_list\x18\x01 \x03(\x0b\x32\x13.flwr.proto.TaskRes\"\x1c\n\x1aPullServerAppInputsRequest\"\x7f\n\x1bPullServerAppInputsResponse\x12$\n\x07\x63ontext\x18\x01 \x01(\x0b\x32\x13.flwr.proto.Context\x12\x1c\n\x03run\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run\x12\x1c\n\x03\x66\x61\x62\x18\x03 \x01(\x0b\x32\x0f.flwr.proto.Fab\"S\n\x1bPushServerAppOutputsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12$\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x13.flwr.proto.Context\"\x1e\n\x1cPushServerAppOutputsResponse2\x9f\x07\n\x0bServerAppIo\x12J\n\tCreateRun\x12\x1c.flwr.proto.CreateRunRequest\x1a\x1d.flwr.proto.CreateRunResponse\"\x00\x12G\n\x08GetNodes\x12\x1b.flwr.proto.GetNodesRequest\x1a\x1c.flwr.proto.GetNodesResponse\"\x00\x12P\n\x0bPushTaskIns\x12\x1e.flwr.proto.PushTaskInsRequest\x1a\x1f.flwr.proto.PushTaskInsResponse\"\x00\x12P\n\x0bPullTaskRes\x12\x1e.flwr.proto.PullTaskResRequest\x1a\x1f.flwr.proto.PullTaskResResponse\"\x00\x12\x41\n\x06GetRun\x12\x19.flwr.proto.GetRunRequest\x1a\x1a.flwr.proto.GetRunResponse\"\x00\x12\x41\n\x06GetFab\x12\x19.flwr.proto.GetFabRequest\x1a\x1a.flwr.proto.GetFabResponse\"\x00\x12h\n\x13PullServerAppInputs\x12&.flwr.proto.PullServerAppInputsRequest\x1a\'.flwr.proto.PullServerAppInputsResponse\"\x00\x12k\n\x14PushServerAppOutputs\x12\'.flwr.proto.PushServerAppOutputsRequest\x1a(.flwr.proto.PushServerAppOutputsResponse\"\x00\x12\\\n\x0fUpdateRunStatus\x12\".flwr.proto.UpdateRunStatusRequest\x1a#.flwr.proto.UpdateRunStatusResponse\"\x00\x12S\n\x0cGetRunStatus\x12\x1f.flwr.proto.GetRunStatusRequest\x1a .flwr.proto.GetRunStatusResponse\"\x00\x12G\n\x08PushLogs\x12\x1b.flwr.proto.PushLogsRequest\x1a\x1c.flwr.proto.PushLogsResponse\"\x00\x62\x06proto3')
|
|
24
24
|
|
|
25
25
|
_globals = globals()
|
|
26
26
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
@@ -32,21 +32,21 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
|
|
32
32
|
_globals['_GETNODESRESPONSE']._serialized_start=217
|
|
33
33
|
_globals['_GETNODESRESPONSE']._serialized_end=268
|
|
34
34
|
_globals['_PUSHTASKINSREQUEST']._serialized_start=270
|
|
35
|
-
_globals['_PUSHTASKINSREQUEST']._serialized_end=
|
|
36
|
-
_globals['_PUSHTASKINSRESPONSE']._serialized_start=
|
|
37
|
-
_globals['_PUSHTASKINSRESPONSE']._serialized_end=
|
|
38
|
-
_globals['_PULLTASKRESREQUEST']._serialized_start=
|
|
39
|
-
_globals['_PULLTASKRESREQUEST']._serialized_end=
|
|
40
|
-
_globals['_PULLTASKRESRESPONSE']._serialized_start=
|
|
41
|
-
_globals['_PULLTASKRESRESPONSE']._serialized_end=
|
|
42
|
-
_globals['_PULLSERVERAPPINPUTSREQUEST']._serialized_start=
|
|
43
|
-
_globals['_PULLSERVERAPPINPUTSREQUEST']._serialized_end=
|
|
44
|
-
_globals['_PULLSERVERAPPINPUTSRESPONSE']._serialized_start=
|
|
45
|
-
_globals['_PULLSERVERAPPINPUTSRESPONSE']._serialized_end=
|
|
46
|
-
_globals['_PUSHSERVERAPPOUTPUTSREQUEST']._serialized_start=
|
|
47
|
-
_globals['_PUSHSERVERAPPOUTPUTSREQUEST']._serialized_end=
|
|
48
|
-
_globals['_PUSHSERVERAPPOUTPUTSRESPONSE']._serialized_start=
|
|
49
|
-
_globals['_PUSHSERVERAPPOUTPUTSRESPONSE']._serialized_end=
|
|
50
|
-
_globals['_SERVERAPPIO']._serialized_start=
|
|
51
|
-
_globals['_SERVERAPPIO']._serialized_end=
|
|
35
|
+
_globals['_PUSHTASKINSREQUEST']._serialized_end=350
|
|
36
|
+
_globals['_PUSHTASKINSRESPONSE']._serialized_start=352
|
|
37
|
+
_globals['_PUSHTASKINSRESPONSE']._serialized_end=391
|
|
38
|
+
_globals['_PULLTASKRESREQUEST']._serialized_start=393
|
|
39
|
+
_globals['_PULLTASKRESREQUEST']._serialized_end=479
|
|
40
|
+
_globals['_PULLTASKRESRESPONSE']._serialized_start=481
|
|
41
|
+
_globals['_PULLTASKRESRESPONSE']._serialized_end=546
|
|
42
|
+
_globals['_PULLSERVERAPPINPUTSREQUEST']._serialized_start=548
|
|
43
|
+
_globals['_PULLSERVERAPPINPUTSREQUEST']._serialized_end=576
|
|
44
|
+
_globals['_PULLSERVERAPPINPUTSRESPONSE']._serialized_start=578
|
|
45
|
+
_globals['_PULLSERVERAPPINPUTSRESPONSE']._serialized_end=705
|
|
46
|
+
_globals['_PUSHSERVERAPPOUTPUTSREQUEST']._serialized_start=707
|
|
47
|
+
_globals['_PUSHSERVERAPPOUTPUTSREQUEST']._serialized_end=790
|
|
48
|
+
_globals['_PUSHSERVERAPPOUTPUTSRESPONSE']._serialized_start=792
|
|
49
|
+
_globals['_PUSHSERVERAPPOUTPUTSRESPONSE']._serialized_end=822
|
|
50
|
+
_globals['_SERVERAPPIO']._serialized_start=825
|
|
51
|
+
_globals['_SERVERAPPIO']._serialized_end=1752
|
|
52
52
|
# @@protoc_insertion_point(module_scope)
|
flwr/proto/serverappio_pb2.pyi
CHANGED
|
@@ -44,13 +44,16 @@ class PushTaskInsRequest(google.protobuf.message.Message):
|
|
|
44
44
|
"""PushTaskIns messages"""
|
|
45
45
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
46
46
|
TASK_INS_LIST_FIELD_NUMBER: builtins.int
|
|
47
|
+
RUN_ID_FIELD_NUMBER: builtins.int
|
|
47
48
|
@property
|
|
48
49
|
def task_ins_list(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[flwr.proto.task_pb2.TaskIns]: ...
|
|
50
|
+
run_id: builtins.int
|
|
49
51
|
def __init__(self,
|
|
50
52
|
*,
|
|
51
53
|
task_ins_list: typing.Optional[typing.Iterable[flwr.proto.task_pb2.TaskIns]] = ...,
|
|
54
|
+
run_id: builtins.int = ...,
|
|
52
55
|
) -> None: ...
|
|
53
|
-
def ClearField(self, field_name: typing_extensions.Literal["task_ins_list",b"task_ins_list"]) -> None: ...
|
|
56
|
+
def ClearField(self, field_name: typing_extensions.Literal["run_id",b"run_id","task_ins_list",b"task_ins_list"]) -> None: ...
|
|
54
57
|
global___PushTaskInsRequest = PushTaskInsRequest
|
|
55
58
|
|
|
56
59
|
class PushTaskInsResponse(google.protobuf.message.Message):
|
|
@@ -70,17 +73,20 @@ class PullTaskResRequest(google.protobuf.message.Message):
|
|
|
70
73
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
71
74
|
NODE_FIELD_NUMBER: builtins.int
|
|
72
75
|
TASK_IDS_FIELD_NUMBER: builtins.int
|
|
76
|
+
RUN_ID_FIELD_NUMBER: builtins.int
|
|
73
77
|
@property
|
|
74
78
|
def node(self) -> flwr.proto.node_pb2.Node: ...
|
|
75
79
|
@property
|
|
76
80
|
def task_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[typing.Text]: ...
|
|
81
|
+
run_id: builtins.int
|
|
77
82
|
def __init__(self,
|
|
78
83
|
*,
|
|
79
84
|
node: typing.Optional[flwr.proto.node_pb2.Node] = ...,
|
|
80
85
|
task_ids: typing.Optional[typing.Iterable[typing.Text]] = ...,
|
|
86
|
+
run_id: builtins.int = ...,
|
|
81
87
|
) -> None: ...
|
|
82
88
|
def HasField(self, field_name: typing_extensions.Literal["node",b"node"]) -> builtins.bool: ...
|
|
83
|
-
def ClearField(self, field_name: typing_extensions.Literal["node",b"node","task_ids",b"task_ids"]) -> None: ...
|
|
89
|
+
def ClearField(self, field_name: typing_extensions.Literal["node",b"node","run_id",b"run_id","task_ids",b"task_ids"]) -> None: ...
|
|
84
90
|
global___PullTaskResRequest = PullTaskResRequest
|
|
85
91
|
|
|
86
92
|
class PullTaskResResponse(google.protobuf.message.Message):
|
|
@@ -62,6 +62,11 @@ class ServerAppIoStub(object):
|
|
|
62
62
|
request_serializer=flwr_dot_proto_dot_run__pb2.UpdateRunStatusRequest.SerializeToString,
|
|
63
63
|
response_deserializer=flwr_dot_proto_dot_run__pb2.UpdateRunStatusResponse.FromString,
|
|
64
64
|
)
|
|
65
|
+
self.GetRunStatus = channel.unary_unary(
|
|
66
|
+
'/flwr.proto.ServerAppIo/GetRunStatus',
|
|
67
|
+
request_serializer=flwr_dot_proto_dot_run__pb2.GetRunStatusRequest.SerializeToString,
|
|
68
|
+
response_deserializer=flwr_dot_proto_dot_run__pb2.GetRunStatusResponse.FromString,
|
|
69
|
+
)
|
|
65
70
|
self.PushLogs = channel.unary_unary(
|
|
66
71
|
'/flwr.proto.ServerAppIo/PushLogs',
|
|
67
72
|
request_serializer=flwr_dot_proto_dot_log__pb2.PushLogsRequest.SerializeToString,
|
|
@@ -135,6 +140,13 @@ class ServerAppIoServicer(object):
|
|
|
135
140
|
context.set_details('Method not implemented!')
|
|
136
141
|
raise NotImplementedError('Method not implemented!')
|
|
137
142
|
|
|
143
|
+
def GetRunStatus(self, request, context):
|
|
144
|
+
"""Get the status of a given run
|
|
145
|
+
"""
|
|
146
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
147
|
+
context.set_details('Method not implemented!')
|
|
148
|
+
raise NotImplementedError('Method not implemented!')
|
|
149
|
+
|
|
138
150
|
def PushLogs(self, request, context):
|
|
139
151
|
"""Push ServerApp logs
|
|
140
152
|
"""
|
|
@@ -190,6 +202,11 @@ def add_ServerAppIoServicer_to_server(servicer, server):
|
|
|
190
202
|
request_deserializer=flwr_dot_proto_dot_run__pb2.UpdateRunStatusRequest.FromString,
|
|
191
203
|
response_serializer=flwr_dot_proto_dot_run__pb2.UpdateRunStatusResponse.SerializeToString,
|
|
192
204
|
),
|
|
205
|
+
'GetRunStatus': grpc.unary_unary_rpc_method_handler(
|
|
206
|
+
servicer.GetRunStatus,
|
|
207
|
+
request_deserializer=flwr_dot_proto_dot_run__pb2.GetRunStatusRequest.FromString,
|
|
208
|
+
response_serializer=flwr_dot_proto_dot_run__pb2.GetRunStatusResponse.SerializeToString,
|
|
209
|
+
),
|
|
193
210
|
'PushLogs': grpc.unary_unary_rpc_method_handler(
|
|
194
211
|
servicer.PushLogs,
|
|
195
212
|
request_deserializer=flwr_dot_proto_dot_log__pb2.PushLogsRequest.FromString,
|
|
@@ -358,6 +375,23 @@ class ServerAppIo(object):
|
|
|
358
375
|
options, channel_credentials,
|
|
359
376
|
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
|
360
377
|
|
|
378
|
+
@staticmethod
|
|
379
|
+
def GetRunStatus(request,
|
|
380
|
+
target,
|
|
381
|
+
options=(),
|
|
382
|
+
channel_credentials=None,
|
|
383
|
+
call_credentials=None,
|
|
384
|
+
insecure=False,
|
|
385
|
+
compression=None,
|
|
386
|
+
wait_for_ready=None,
|
|
387
|
+
timeout=None,
|
|
388
|
+
metadata=None):
|
|
389
|
+
return grpc.experimental.unary_unary(request, target, '/flwr.proto.ServerAppIo/GetRunStatus',
|
|
390
|
+
flwr_dot_proto_dot_run__pb2.GetRunStatusRequest.SerializeToString,
|
|
391
|
+
flwr_dot_proto_dot_run__pb2.GetRunStatusResponse.FromString,
|
|
392
|
+
options, channel_credentials,
|
|
393
|
+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
|
394
|
+
|
|
361
395
|
@staticmethod
|
|
362
396
|
def PushLogs(request,
|
|
363
397
|
target,
|
|
@@ -56,6 +56,11 @@ class ServerAppIoStub:
|
|
|
56
56
|
flwr.proto.run_pb2.UpdateRunStatusResponse]
|
|
57
57
|
"""Update the status of a given run"""
|
|
58
58
|
|
|
59
|
+
GetRunStatus: grpc.UnaryUnaryMultiCallable[
|
|
60
|
+
flwr.proto.run_pb2.GetRunStatusRequest,
|
|
61
|
+
flwr.proto.run_pb2.GetRunStatusResponse]
|
|
62
|
+
"""Get the status of a given run"""
|
|
63
|
+
|
|
59
64
|
PushLogs: grpc.UnaryUnaryMultiCallable[
|
|
60
65
|
flwr.proto.log_pb2.PushLogsRequest,
|
|
61
66
|
flwr.proto.log_pb2.PushLogsResponse]
|
|
@@ -135,6 +140,14 @@ class ServerAppIoServicer(metaclass=abc.ABCMeta):
|
|
|
135
140
|
"""Update the status of a given run"""
|
|
136
141
|
pass
|
|
137
142
|
|
|
143
|
+
@abc.abstractmethod
|
|
144
|
+
def GetRunStatus(self,
|
|
145
|
+
request: flwr.proto.run_pb2.GetRunStatusRequest,
|
|
146
|
+
context: grpc.ServicerContext,
|
|
147
|
+
) -> flwr.proto.run_pb2.GetRunStatusResponse:
|
|
148
|
+
"""Get the status of a given run"""
|
|
149
|
+
pass
|
|
150
|
+
|
|
138
151
|
@abc.abstractmethod
|
|
139
152
|
def PushLogs(self,
|
|
140
153
|
request: flwr.proto.log_pb2.PushLogsRequest,
|
|
@@ -203,7 +203,9 @@ class GrpcDriver(Driver):
|
|
|
203
203
|
task_ins_list.append(taskins)
|
|
204
204
|
# Call GrpcDriverStub method
|
|
205
205
|
res: PushTaskInsResponse = self._stub.PushTaskIns(
|
|
206
|
-
PushTaskInsRequest(
|
|
206
|
+
PushTaskInsRequest(
|
|
207
|
+
task_ins_list=task_ins_list, run_id=cast(Run, self._run).run_id
|
|
208
|
+
)
|
|
207
209
|
)
|
|
208
210
|
return list(res.task_ids)
|
|
209
211
|
|
|
@@ -215,7 +217,9 @@ class GrpcDriver(Driver):
|
|
|
215
217
|
"""
|
|
216
218
|
# Pull TaskRes
|
|
217
219
|
res: PullTaskResResponse = self._stub.PullTaskRes(
|
|
218
|
-
PullTaskResRequest(
|
|
220
|
+
PullTaskResRequest(
|
|
221
|
+
node=self.node, task_ids=message_ids, run_id=cast(Run, self._run).run_id
|
|
222
|
+
)
|
|
219
223
|
)
|
|
220
224
|
# Convert TaskRes to Message
|
|
221
225
|
msgs = [message_from_taskres(taskres) for taskres in res.task_res_list]
|
|
@@ -32,6 +32,7 @@ from flwr.common.serde import (
|
|
|
32
32
|
fab_from_proto,
|
|
33
33
|
fab_to_proto,
|
|
34
34
|
run_status_from_proto,
|
|
35
|
+
run_status_to_proto,
|
|
35
36
|
run_to_proto,
|
|
36
37
|
user_config_from_proto,
|
|
37
38
|
)
|
|
@@ -48,6 +49,8 @@ from flwr.proto.run_pb2 import ( # pylint: disable=E0611
|
|
|
48
49
|
CreateRunResponse,
|
|
49
50
|
GetRunRequest,
|
|
50
51
|
GetRunResponse,
|
|
52
|
+
GetRunStatusRequest,
|
|
53
|
+
GetRunStatusResponse,
|
|
51
54
|
UpdateRunStatusRequest,
|
|
52
55
|
UpdateRunStatusResponse,
|
|
53
56
|
)
|
|
@@ -284,6 +287,21 @@ class ServerAppIoServicer(serverappio_pb2_grpc.ServerAppIoServicer):
|
|
|
284
287
|
state.add_serverapp_log(request.run_id, merged_logs)
|
|
285
288
|
return PushLogsResponse()
|
|
286
289
|
|
|
290
|
+
def GetRunStatus(
|
|
291
|
+
self, request: GetRunStatusRequest, context: grpc.ServicerContext
|
|
292
|
+
) -> GetRunStatusResponse:
|
|
293
|
+
"""Get the status of a run."""
|
|
294
|
+
log(DEBUG, "ServerAppIoServicer.GetRunStatus")
|
|
295
|
+
state = self.state_factory.state()
|
|
296
|
+
|
|
297
|
+
# Get run status from LinkState
|
|
298
|
+
run_statuses = state.get_run_status(set(request.run_ids))
|
|
299
|
+
run_status_dict = {
|
|
300
|
+
run_id: run_status_to_proto(run_status)
|
|
301
|
+
for run_id, run_status in run_statuses.items()
|
|
302
|
+
}
|
|
303
|
+
return GetRunStatusResponse(run_status_dict=run_status_dict)
|
|
304
|
+
|
|
287
305
|
|
|
288
306
|
def _raise_if(validation_error: bool, detail: str) -> None:
|
|
289
307
|
if validation_error:
|
{flwr_nightly-1.14.0.dev20241210.dist-info → flwr_nightly-1.14.0.dev20241211.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: flwr-nightly
|
|
3
|
-
Version: 1.14.0.
|
|
3
|
+
Version: 1.14.0.dev20241211
|
|
4
4
|
Summary: Flower: A Friendly Federated AI Framework
|
|
5
5
|
Home-page: https://flower.ai
|
|
6
6
|
License: Apache-2.0
|
|
@@ -102,23 +102,23 @@ Flower's goal is to make federated learning accessible to everyone. This series
|
|
|
102
102
|
|
|
103
103
|
0. **What is Federated Learning?**
|
|
104
104
|
|
|
105
|
-
[](https://colab.research.google.com/github/adap/flower/blob/main/
|
|
105
|
+
[](https://colab.research.google.com/github/adap/flower/blob/main/framework/docs/source/tutorial-series-what-is-federated-learning.ipynb) (or open the [Jupyter Notebook](https://github.com/adap/flower/blob/main/framework/docs/source/tutorial-series-what-is-federated-learning.ipynb))
|
|
106
106
|
|
|
107
107
|
1. **An Introduction to Federated Learning**
|
|
108
108
|
|
|
109
|
-
[](https://colab.research.google.com/github/adap/flower/blob/main/
|
|
109
|
+
[](https://colab.research.google.com/github/adap/flower/blob/main/framework/docs/source/tutorial-series-get-started-with-flower-pytorch.ipynb) (or open the [Jupyter Notebook](https://github.com/adap/flower/blob/main/framework/docs/source/tutorial-series-get-started-with-flower-pytorch.ipynb))
|
|
110
110
|
|
|
111
111
|
2. **Using Strategies in Federated Learning**
|
|
112
112
|
|
|
113
|
-
[](https://colab.research.google.com/github/adap/flower/blob/main/
|
|
113
|
+
[](https://colab.research.google.com/github/adap/flower/blob/main/framework/docs/source/tutorial-series-use-a-federated-learning-strategy-pytorch.ipynb) (or open the [Jupyter Notebook](https://github.com/adap/flower/blob/main/framework/docs/source/tutorial-series-use-a-federated-learning-strategy-pytorch.ipynb))
|
|
114
114
|
|
|
115
115
|
3. **Building Strategies for Federated Learning**
|
|
116
116
|
|
|
117
|
-
[](https://colab.research.google.com/github/adap/flower/blob/main/
|
|
117
|
+
[](https://colab.research.google.com/github/adap/flower/blob/main/framework/docs/source/tutorial-series-build-a-strategy-from-scratch-pytorch.ipynb) (or open the [Jupyter Notebook](https://github.com/adap/flower/blob/main/framework/docs/source/tutorial-series-build-a-strategy-from-scratch-pytorch.ipynb))
|
|
118
118
|
|
|
119
119
|
4. **Custom Clients for Federated Learning**
|
|
120
120
|
|
|
121
|
-
[](https://colab.research.google.com/github/adap/flower/blob/main/doc/source/tutorial-series-customize-the-client-pytorch.ipynb) (or open the [Jupyter Notebook](https://github.com/adap/flower/blob/main/
|
|
121
|
+
[](https://colab.research.google.com/github/adap/flower/blob/main/doc/source/tutorial-series-customize-the-client-pytorch.ipynb) (or open the [Jupyter Notebook](https://github.com/adap/flower/blob/main/framework/docs/source/tutorial-series-customize-the-client-pytorch.ipynb))
|
|
122
122
|
|
|
123
123
|
Stay tuned, more tutorials are coming soon. Topics include **Privacy and Security in Federated Learning**, and **Scaling Federated Learning**.
|
|
124
124
|
|
{flwr_nightly-1.14.0.dev20241210.dist-info → flwr_nightly-1.14.0.dev20241211.dist-info}/RECORD
RENAMED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
flwr/__init__.py,sha256=VmBWedrCxqmt4QvUHBLqyVEH6p7zaFMD_oCHerXHSVw,937
|
|
2
2
|
flwr/cli/__init__.py,sha256=cZJVgozlkC6Ni2Hd_FAIrqefrkCGOV18fikToq-6iLw,720
|
|
3
|
-
flwr/cli/app.py,sha256=
|
|
3
|
+
flwr/cli/app.py,sha256=KF__zHSy7KQCMx_Rb0YPzcoZbQY-Zo4f70BhBgP4ENM,1381
|
|
4
4
|
flwr/cli/build.py,sha256=k2M0aIY2q5WB_yXQ22Woxt1zb6m-Z1wNwmhWMxEm5Dw,6344
|
|
5
|
-
flwr/cli/
|
|
5
|
+
flwr/cli/cli_user_auth_interceptor.py,sha256=rEjgAZmzHO0GjwdyZib6bkTI2X59ErJAZlutqpqZGF0,2952
|
|
6
|
+
flwr/cli/config_utils.py,sha256=f4ViGujhEat9l3YDq24AE-hao4pAK_hVLRXZXDd_F_A,12078
|
|
6
7
|
flwr/cli/example.py,sha256=1bGDYll3BXQY2kRqSN-oICqS5n1b9m0g0RvXTopXHl4,2215
|
|
7
8
|
flwr/cli/install.py,sha256=kmD2dW-9B7645GAQx5es1o2W11gRHQ2Fg2E31SLomrg,8179
|
|
8
|
-
flwr/cli/log.py,sha256=
|
|
9
|
-
flwr/cli/
|
|
9
|
+
flwr/cli/log.py,sha256=7V5NPGiR8FMDkkNTc5SE1pxMskQgp0H5HniG977LISo,5994
|
|
10
|
+
flwr/cli/login/__init__.py,sha256=PEh6QjLSx7ltN8d8Jfi25dHFPKtCNKjYJZCkYQBfmm0,799
|
|
11
|
+
flwr/cli/login/login.py,sha256=GVm6rkLDVQ6WuT2mw52gBKNW_Y5IjGg_OOGoEmpx9KM,2796
|
|
12
|
+
flwr/cli/ls.py,sha256=5uO0YG0XXn7paS4oUs1T7rwicApxMV3ac9ejBZfLN3k,10545
|
|
10
13
|
flwr/cli/new/__init__.py,sha256=cQzK1WH4JP2awef1t2UQ2xjl1agVEz9rwutV18SWV1k,789
|
|
11
14
|
flwr/cli/new/new.py,sha256=xgzObnhNpnGvjVs6wTj6BlJ9X-avPhRX3DuwWnk9ED0,9903
|
|
12
15
|
flwr/cli/new/templates/__init__.py,sha256=4luU8RL-CK8JJCstQ_ON809W9bNTkY1l9zSaPKBkgwY,725
|
|
@@ -62,9 +65,9 @@ flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=UtH3Vslg2S8fIKIHC-d
|
|
|
62
65
|
flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl,sha256=01HArBqRrbZT3O7pXOM9MqduXMNm525wv7Sj6dvYMJE,686
|
|
63
66
|
flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=KVCIOEYNWnq6j7XOboXqZshc9aQ2PyRDUu7bZtmfJ24,710
|
|
64
67
|
flwr/cli/run/__init__.py,sha256=oCd6HmQDx-sqver1gecgx-uMA38BLTSiiKpl7RGNceg,789
|
|
65
|
-
flwr/cli/run/run.py,sha256=
|
|
66
|
-
flwr/cli/stop.py,sha256=
|
|
67
|
-
flwr/cli/utils.py,sha256=
|
|
68
|
+
flwr/cli/run/run.py,sha256=sklMcREZnnuot4TSeCIpVYqZJ2Ar2SOye3q0lw5s218,8139
|
|
69
|
+
flwr/cli/stop.py,sha256=pa3etMLCLxfGl9w2c2o6e5u46j6LimEmNp2zuQGxAIk,3143
|
|
70
|
+
flwr/cli/utils.py,sha256=hiT4-tDPmS5U_mH0ED9DVTElfmfYUMV6JuPWZjTaIgQ,8330
|
|
68
71
|
flwr/client/__init__.py,sha256=DGDoO0AEAfz-0CUFmLdyUUweAS64-07AOnmDfWUefK4,1192
|
|
69
72
|
flwr/client/app.py,sha256=3AKrJduvki_1ATvKCQV4T9_1qZuarVVTtpnsq6_cWw0,34384
|
|
70
73
|
flwr/client/client.py,sha256=gy6WVlMUFAp8oevN4xpQPX30vPOIYGVqdbuFlTWkyG4,9080
|
|
@@ -110,8 +113,8 @@ flwr/common/address.py,sha256=7kM2Rqjw86-c8aKwAvrXerWqznnVv4TFJ62aSAeTn10,3017
|
|
|
110
113
|
flwr/common/args.py,sha256=-KeQ6AZw1-G4Ifhsg4qlRnWhGH1m_OzUgxH7Z4j_0ns,6222
|
|
111
114
|
flwr/common/auth_plugin/__init__.py,sha256=1Y8Oj3iB49IHDu9tvDih1J74Ygu7k85V9s2A4WORPyA,887
|
|
112
115
|
flwr/common/auth_plugin/auth_plugin.py,sha256=6WEAVVPrS7LgSBpd4WyHYU4EsajT2nBGI_IN3mhYzoU,3567
|
|
113
|
-
flwr/common/config.py,sha256=
|
|
114
|
-
flwr/common/constant.py,sha256
|
|
116
|
+
flwr/common/config.py,sha256=kH8u7VBRfyv5cpOC0lQ1jBbxJ6Jo2b3XhUwbIbHoHHw,8098
|
|
117
|
+
flwr/common/constant.py,sha256=9HwFVxFWbLTzMetIffUT3gAC9nPtqzBNxrKWr5A0oSI,5996
|
|
115
118
|
flwr/common/context.py,sha256=uJ-mnoC_8y_udEb3kAX-r8CPphNTWM72z1AlsvQEu54,2403
|
|
116
119
|
flwr/common/date.py,sha256=NHHpESce5wYqEwoDXf09gp9U9l_5Bmlh2BsOcwS-kDM,1554
|
|
117
120
|
flwr/common/differential_privacy.py,sha256=XwcJ3rWr8S8BZUocc76vLSJAXIf6OHnWkBV6-xlIRuw,6106
|
|
@@ -140,9 +143,9 @@ flwr/common/secure_aggregation/crypto/symmetric_encryption.py,sha256=wTDbOaMGZwT
|
|
|
140
143
|
flwr/common/secure_aggregation/ndarrays_arithmetic.py,sha256=zvVAIrIyI6OSzGhpCi8NNaTvPXmoMYQIPJT-NkBg8RU,3013
|
|
141
144
|
flwr/common/secure_aggregation/quantization.py,sha256=mC4uLf05zeONo8Ke-BY0Tj8UCMOS7VD93zHCzuv3MHU,2304
|
|
142
145
|
flwr/common/secure_aggregation/secaggplus_constants.py,sha256=9MF-oQh62uD7rt9VeNB-rHf2gBLd5GL3S9OejCxmILY,2183
|
|
143
|
-
flwr/common/secure_aggregation/secaggplus_utils.py,sha256=
|
|
146
|
+
flwr/common/secure_aggregation/secaggplus_utils.py,sha256=OgYd68YBRaHQYLc-YdExj9CSpwL58bVTaPrdHoAj2AE,3214
|
|
144
147
|
flwr/common/serde.py,sha256=K9ExsqcTPETESkt2HMaNtIQAIAfwmuwtJFlG-59I7Sw,31046
|
|
145
|
-
flwr/common/telemetry.py,sha256=
|
|
148
|
+
flwr/common/telemetry.py,sha256=CHIwFFQ13sWFavmEvkvA43XR1sbh1S3nWvD5TuCO2eI,8774
|
|
146
149
|
flwr/common/typing.py,sha256=RLq2f9jhE_Nndtk023cPMG0LpoQHaacEsww-3j0xs4Q,5710
|
|
147
150
|
flwr/common/version.py,sha256=tCcl_FvxVK206C1dxIJCs4TjL06WmyaODBP19FRHE1c,1324
|
|
148
151
|
flwr/proto/__init__.py,sha256=hbY7JYakwZwCkYgCNlmHdc8rtvfoJbAZLalMdc--CGc,683
|
|
@@ -190,10 +193,10 @@ flwr/proto/run_pb2.py,sha256=J2TQwf-S0o9ImGuQLrczw99S0GSXm6hk-npJ8rXAC0Y,5743
|
|
|
190
193
|
flwr/proto/run_pb2.pyi,sha256=i6TEwphuFH94_kT2hZWb_RjndLuphkPrT3C2VP-NnVs,11739
|
|
191
194
|
flwr/proto/run_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
|
192
195
|
flwr/proto/run_pb2_grpc.pyi,sha256=ff2TSiLVnG6IVQcTGzb2DIH3XRSoAvAo_RMcvbMFyc0,76
|
|
193
|
-
flwr/proto/serverappio_pb2.py,sha256=
|
|
194
|
-
flwr/proto/serverappio_pb2.pyi,sha256=
|
|
195
|
-
flwr/proto/serverappio_pb2_grpc.py,sha256=
|
|
196
|
-
flwr/proto/serverappio_pb2_grpc.pyi,sha256=
|
|
196
|
+
flwr/proto/serverappio_pb2.py,sha256=VXJxFLDrH4XzCEM9VnNg3z7gVIsrvl1VE4ZtK_lI_eE,5003
|
|
197
|
+
flwr/proto/serverappio_pb2.pyi,sha256=5SoXVb7fyN0xkl411pmJChlWeQ-sr1Qf9J3B0cAQYc4,6665
|
|
198
|
+
flwr/proto/serverappio_pb2_grpc.py,sha256=fGmk0XC7il5wYFSo6zEa21Ki1OYvEuDDXL0hDDoU4QQ,19062
|
|
199
|
+
flwr/proto/serverappio_pb2_grpc.pyi,sha256=pKdqFpAgkHaNSjRNahnGtSndUif8uB5eFui_q37eDho,5220
|
|
197
200
|
flwr/proto/simulationio_pb2.py,sha256=Fv7m8d4vR_0CIGU93nN5tDXSCk2QPbASH_8mT2wBPTE,3117
|
|
198
201
|
flwr/proto/simulationio_pb2.pyi,sha256=oXx8_FLBe5B54wduZj-f89kub73XxNtQbThuW8YfPAs,2660
|
|
199
202
|
flwr/proto/simulationio_pb2_grpc.py,sha256=9I3yAfJaeMuG-qH_5Ge45eFOftsIOmL9b8E_xHmcvKw,11232
|
|
@@ -219,7 +222,7 @@ flwr/server/compat/legacy_context.py,sha256=wBzBcfV6YO6IQGriM_FdJ5XZfiBBEEJdS_Od
|
|
|
219
222
|
flwr/server/criterion.py,sha256=ypbAexbztzGUxNen9RCHF91QeqiEQix4t4Ih3E-42MM,1061
|
|
220
223
|
flwr/server/driver/__init__.py,sha256=bikRv6CjTwSvYh7tf10gziU5o2YotOWhhftz2tr3KDc,886
|
|
221
224
|
flwr/server/driver/driver.py,sha256=u_fMfqLYTroTafGCNwKPHI4lttRL-Z5CqeT3_FHSq-Q,5701
|
|
222
|
-
flwr/server/driver/grpc_driver.py,sha256=
|
|
225
|
+
flwr/server/driver/grpc_driver.py,sha256=cMYtyQJRSwfhCmtJ5UEWN4iXrUKRH5iGXmigiU6sGjM,11529
|
|
223
226
|
flwr/server/driver/inmemory_driver.py,sha256=gfB4jmkk1indhRa9XCdKCXghVcWBF1qBD-tAxMUyQm0,6404
|
|
224
227
|
flwr/server/history.py,sha256=qSb5_pPTrwofpSYGsZWzMPkl_4uJ4mJFWesxXDrEvDU,5026
|
|
225
228
|
flwr/server/run_serverapp.py,sha256=oDfHaHyVT5BRcckFFQKg8AVPCWR1ek7OhNceTC8qq9g,2493
|
|
@@ -256,7 +259,7 @@ flwr/server/strategy/strategy.py,sha256=cXapkD5uDrt5C-RbmWDn9FLoap3Q41i7GKvbmfbC
|
|
|
256
259
|
flwr/server/superlink/__init__.py,sha256=8tHYCfodUlRD8PCP9fHgvu8cz5N31A2QoRVL0jDJ15E,707
|
|
257
260
|
flwr/server/superlink/driver/__init__.py,sha256=5soEK5QSvxNjmJQ-CGTWROc4alSAeU0e9Ad9RDhsd3E,717
|
|
258
261
|
flwr/server/superlink/driver/serverappio_grpc.py,sha256=oTogZLkfeThKdx9Q_bw6OMGHnLIryxQOHxbWi0qgaRM,2185
|
|
259
|
-
flwr/server/superlink/driver/serverappio_servicer.py,sha256=
|
|
262
|
+
flwr/server/superlink/driver/serverappio_servicer.py,sha256=MSJPcSDim36sXPoK21XmhHYZwWI-i9Z5NiZrvyBRJyc,11124
|
|
260
263
|
flwr/server/superlink/ffs/__init__.py,sha256=FAY-zShcfPmOxosok2QyT6hTNMNctG8cH9s_nIl8jkI,840
|
|
261
264
|
flwr/server/superlink/ffs/disk_ffs.py,sha256=yCN6CCzegnJIOaHr5nIu49wZQa4g5BByiSKshz50RKU,3296
|
|
262
265
|
flwr/server/superlink/ffs/ffs.py,sha256=qLI1UfosJugu2BKOJWqHIhafTm-YiuKqGf3OGWPH0NM,2395
|
|
@@ -317,8 +320,8 @@ flwr/superexec/exec_servicer.py,sha256=jEYcASzkQR1ftjzilmlcTPKXo8NSno9mSj_UbBvMj
|
|
|
317
320
|
flwr/superexec/exec_user_auth_interceptor.py,sha256=K06OU-l4LnYhTDg071hGJuOaQWEJbZsYi5qxUmmtiG0,3704
|
|
318
321
|
flwr/superexec/executor.py,sha256=zH3_53il6Jh0ZscIVEB9f4GNnXMeBbCGyCoBCxLgiG0,3114
|
|
319
322
|
flwr/superexec/simulation.py,sha256=WQDon15oqpMopAZnwRZoTICYCfHqtkvFSqiTQ2hLD_g,4088
|
|
320
|
-
flwr_nightly-1.14.0.
|
|
321
|
-
flwr_nightly-1.14.0.
|
|
322
|
-
flwr_nightly-1.14.0.
|
|
323
|
-
flwr_nightly-1.14.0.
|
|
324
|
-
flwr_nightly-1.14.0.
|
|
323
|
+
flwr_nightly-1.14.0.dev20241211.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
324
|
+
flwr_nightly-1.14.0.dev20241211.dist-info/METADATA,sha256=onYC4ZwXpA_wegLQm_jOd4kFEDQtuJAHOsZ3wYWmue0,15799
|
|
325
|
+
flwr_nightly-1.14.0.dev20241211.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
326
|
+
flwr_nightly-1.14.0.dev20241211.dist-info/entry_points.txt,sha256=JlNxX3qhaV18_2yj5a3kJW1ESxm31cal9iS_N_pf1Rk,538
|
|
327
|
+
flwr_nightly-1.14.0.dev20241211.dist-info/RECORD,,
|
{flwr_nightly-1.14.0.dev20241210.dist-info → flwr_nightly-1.14.0.dev20241211.dist-info}/LICENSE
RENAMED
|
File without changes
|
{flwr_nightly-1.14.0.dev20241210.dist-info → flwr_nightly-1.14.0.dev20241211.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|