flwr 1.24.0__py3-none-any.whl → 1.26.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flwr/__init__.py +1 -1
- flwr/app/__init__.py +4 -1
- flwr/app/message_type.py +29 -0
- flwr/app/metadata.py +5 -2
- flwr/app/user_config.py +19 -0
- flwr/cli/app.py +37 -19
- flwr/cli/app_cmd/publish.py +25 -75
- flwr/cli/app_cmd/review.py +25 -66
- flwr/cli/auth_plugin/auth_plugin.py +5 -10
- flwr/cli/auth_plugin/noop_auth_plugin.py +1 -2
- flwr/cli/auth_plugin/oidc_cli_plugin.py +38 -38
- flwr/cli/build.py +15 -28
- flwr/cli/config/__init__.py +21 -0
- flwr/cli/config/ls.py +71 -0
- flwr/cli/config_migration.py +297 -0
- flwr/cli/config_utils.py +63 -156
- flwr/cli/constant.py +71 -0
- flwr/cli/federation/__init__.py +0 -2
- flwr/cli/federation/ls.py +256 -64
- flwr/cli/flower_config.py +429 -0
- flwr/cli/install.py +23 -62
- flwr/cli/log.py +23 -37
- flwr/cli/login/login.py +29 -63
- flwr/cli/ls.py +72 -61
- flwr/cli/new/new.py +98 -309
- flwr/cli/pull.py +19 -37
- flwr/cli/run/run.py +87 -100
- flwr/cli/run_utils.py +23 -5
- flwr/cli/stop.py +33 -74
- flwr/cli/supernode/ls.py +35 -62
- flwr/cli/supernode/register.py +31 -80
- flwr/cli/supernode/unregister.py +24 -70
- flwr/cli/typing.py +200 -0
- flwr/cli/utils.py +160 -412
- flwr/client/grpc_adapter_client/connection.py +2 -2
- flwr/client/grpc_rere_client/connection.py +9 -6
- flwr/client/grpc_rere_client/grpc_adapter.py +1 -1
- flwr/client/message_handler/message_handler.py +2 -1
- flwr/client/mod/centraldp_mods.py +1 -1
- flwr/client/mod/localdp_mod.py +1 -1
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
- flwr/client/rest_client/connection.py +6 -4
- flwr/client/run_info_store.py +2 -1
- flwr/clientapp/client_app.py +2 -1
- flwr/common/__init__.py +3 -2
- flwr/common/args.py +5 -5
- flwr/common/config.py +12 -17
- flwr/common/constant.py +3 -16
- flwr/common/context.py +2 -1
- flwr/common/exit/exit.py +4 -4
- flwr/common/exit/exit_code.py +6 -0
- flwr/common/grpc.py +2 -1
- flwr/common/logger.py +1 -1
- flwr/common/message.py +1 -1
- flwr/common/retry_invoker.py +13 -5
- flwr/common/secure_aggregation/ndarrays_arithmetic.py +5 -2
- flwr/common/serde.py +13 -5
- flwr/common/telemetry.py +1 -1
- flwr/common/typing.py +10 -3
- flwr/compat/client/app.py +6 -9
- flwr/compat/client/grpc_client/connection.py +2 -1
- flwr/compat/common/constant.py +29 -0
- flwr/compat/server/app.py +1 -1
- flwr/proto/clientappio_pb2.py +2 -2
- flwr/proto/clientappio_pb2_grpc.py +104 -88
- flwr/proto/clientappio_pb2_grpc.pyi +140 -80
- flwr/proto/federation_pb2.py +5 -3
- flwr/proto/federation_pb2.pyi +32 -2
- flwr/proto/fleet_pb2.py +10 -10
- flwr/proto/fleet_pb2.pyi +5 -1
- flwr/proto/run_pb2.py +18 -26
- flwr/proto/run_pb2.pyi +10 -58
- flwr/proto/serverappio_pb2.py +2 -2
- flwr/proto/serverappio_pb2_grpc.py +138 -207
- flwr/proto/serverappio_pb2_grpc.pyi +189 -155
- flwr/proto/simulationio_pb2.py +2 -2
- flwr/proto/simulationio_pb2_grpc.py +62 -90
- flwr/proto/simulationio_pb2_grpc.pyi +95 -55
- flwr/server/app.py +7 -13
- flwr/server/compat/grid_client_proxy.py +2 -1
- flwr/server/grid/grpc_grid.py +5 -5
- flwr/server/serverapp/app.py +11 -4
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +1 -1
- flwr/server/superlink/fleet/grpc_rere/node_auth_server_interceptor.py +13 -12
- flwr/server/superlink/fleet/message_handler/message_handler.py +42 -2
- flwr/server/superlink/linkstate/__init__.py +2 -2
- flwr/server/superlink/linkstate/in_memory_linkstate.py +36 -10
- flwr/server/superlink/linkstate/linkstate.py +34 -21
- flwr/server/superlink/linkstate/linkstate_factory.py +16 -8
- flwr/server/superlink/linkstate/{sqlite_linkstate.py → sql_linkstate.py} +471 -516
- flwr/server/superlink/linkstate/utils.py +49 -2
- flwr/server/superlink/serverappio/serverappio_servicer.py +1 -33
- flwr/server/superlink/simulation/simulationio_servicer.py +0 -19
- flwr/server/utils/validator.py +1 -1
- flwr/server/workflow/default_workflows.py +2 -1
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +1 -1
- flwr/serverapp/strategy/bulyan.py +7 -1
- flwr/serverapp/strategy/dp_fixed_clipping.py +9 -1
- flwr/serverapp/strategy/fedavg.py +1 -1
- flwr/serverapp/strategy/fedxgb_cyclic.py +1 -1
- flwr/simulation/ray_transport/ray_client_proxy.py +2 -6
- flwr/simulation/run_simulation.py +3 -12
- flwr/simulation/simulationio_connection.py +3 -3
- flwr/{common → supercore}/address.py +7 -33
- flwr/supercore/app_utils.py +2 -1
- flwr/supercore/constant.py +27 -2
- flwr/supercore/corestate/{sqlite_corestate.py → sql_corestate.py} +19 -23
- flwr/supercore/credential_store/__init__.py +33 -0
- flwr/supercore/credential_store/credential_store.py +34 -0
- flwr/supercore/credential_store/file_credential_store.py +76 -0
- flwr/{common → supercore}/date.py +0 -11
- flwr/supercore/ffs/disk_ffs.py +1 -1
- flwr/supercore/object_store/object_store_factory.py +14 -6
- flwr/supercore/object_store/{sqlite_object_store.py → sql_object_store.py} +115 -117
- flwr/supercore/sql_mixin.py +315 -0
- flwr/{cli/new/templates → supercore/state}/__init__.py +2 -2
- flwr/{cli/new/templates/app/code/flwr_tune → supercore/state/alembic}/__init__.py +2 -2
- flwr/supercore/state/alembic/env.py +103 -0
- flwr/supercore/state/alembic/script.py.mako +43 -0
- flwr/supercore/state/alembic/utils.py +239 -0
- flwr/{cli/new/templates/app → supercore/state/alembic/versions}/__init__.py +2 -2
- flwr/supercore/state/alembic/versions/rev_2026_01_28_initialize_migration_of_state_tables.py +200 -0
- flwr/supercore/state/schema/README.md +121 -0
- flwr/{cli/new/templates/app/code → supercore/state/schema}/__init__.py +2 -2
- flwr/supercore/state/schema/corestate_tables.py +36 -0
- flwr/supercore/state/schema/linkstate_tables.py +152 -0
- flwr/supercore/state/schema/objectstore_tables.py +90 -0
- flwr/supercore/superexec/run_superexec.py +2 -2
- flwr/supercore/utils.py +225 -0
- flwr/superlink/federation/federation_manager.py +2 -2
- flwr/superlink/federation/noop_federation_manager.py +8 -6
- flwr/superlink/servicer/control/control_grpc.py +2 -0
- flwr/superlink/servicer/control/control_servicer.py +106 -21
- flwr/supernode/cli/flower_supernode.py +2 -1
- flwr/supernode/nodestate/in_memory_nodestate.py +62 -1
- flwr/supernode/nodestate/nodestate.py +45 -0
- flwr/supernode/runtime/run_clientapp.py +14 -14
- flwr/supernode/servicer/clientappio/clientappio_servicer.py +13 -5
- flwr/supernode/start_client_internal.py +17 -10
- {flwr-1.24.0.dist-info → flwr-1.26.0.dist-info}/METADATA +8 -8
- {flwr-1.24.0.dist-info → flwr-1.26.0.dist-info}/RECORD +144 -184
- flwr/cli/federation/show.py +0 -317
- flwr/cli/new/templates/app/.gitignore.tpl +0 -163
- flwr/cli/new/templates/app/LICENSE.tpl +0 -202
- flwr/cli/new/templates/app/README.baseline.md.tpl +0 -127
- flwr/cli/new/templates/app/README.flowertune.md.tpl +0 -68
- flwr/cli/new/templates/app/README.md.tpl +0 -37
- flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/code/__init__.py.tpl +0 -1
- flwr/cli/new/templates/app/code/__init__.pytorch_legacy_api.py.tpl +0 -1
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +0 -75
- flwr/cli/new/templates/app/code/client.huggingface.py.tpl +0 -93
- flwr/cli/new/templates/app/code/client.jax.py.tpl +0 -71
- flwr/cli/new/templates/app/code/client.mlx.py.tpl +0 -102
- flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -46
- flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -80
- flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +0 -55
- flwr/cli/new/templates/app/code/client.sklearn.py.tpl +0 -108
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -82
- flwr/cli/new/templates/app/code/client.xgboost.py.tpl +0 -110
- flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +0 -36
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +0 -92
- flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +0 -87
- flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +0 -56
- flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +0 -73
- flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +0 -78
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -66
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +0 -43
- flwr/cli/new/templates/app/code/server.huggingface.py.tpl +0 -42
- flwr/cli/new/templates/app/code/server.jax.py.tpl +0 -39
- flwr/cli/new/templates/app/code/server.mlx.py.tpl +0 -41
- flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -38
- flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -41
- flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +0 -31
- flwr/cli/new/templates/app/code/server.sklearn.py.tpl +0 -44
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -38
- flwr/cli/new/templates/app/code/server.xgboost.py.tpl +0 -56
- flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/code/task.huggingface.py.tpl +0 -98
- flwr/cli/new/templates/app/code/task.jax.py.tpl +0 -57
- flwr/cli/new/templates/app/code/task.mlx.py.tpl +0 -102
- flwr/cli/new/templates/app/code/task.numpy.py.tpl +0 -7
- flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -99
- flwr/cli/new/templates/app/code/task.pytorch_legacy_api.py.tpl +0 -111
- flwr/cli/new/templates/app/code/task.sklearn.py.tpl +0 -67
- flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +0 -52
- flwr/cli/new/templates/app/code/task.xgboost.py.tpl +0 -67
- flwr/cli/new/templates/app/code/utils.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +0 -146
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +0 -80
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +0 -65
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +0 -52
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +0 -56
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +0 -49
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +0 -52
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +0 -61
- flwr/common/pyproject.py +0 -42
- flwr/supercore/sqlite_mixin.py +0 -159
- /flwr/{common → supercore}/version.py +0 -0
- {flwr-1.24.0.dist-info → flwr-1.26.0.dist-info}/WHEEL +0 -0
- {flwr-1.24.0.dist-info → flwr-1.26.0.dist-info}/entry_points.txt +0 -0
flwr/cli/run/run.py
CHANGED
|
@@ -16,23 +16,23 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
import hashlib
|
|
19
|
-
import io
|
|
20
19
|
import json
|
|
21
20
|
import subprocess
|
|
22
21
|
from pathlib import Path
|
|
23
|
-
from typing import Annotated, Any
|
|
22
|
+
from typing import Annotated, Any
|
|
24
23
|
|
|
24
|
+
import click
|
|
25
25
|
import typer
|
|
26
|
-
from rich.console import Console
|
|
27
26
|
|
|
28
27
|
from flwr.cli.build import build_fab_from_disk, get_fab_filename
|
|
29
|
-
from flwr.cli.
|
|
30
|
-
from flwr.cli.config_utils import
|
|
31
|
-
load_and_validate,
|
|
32
|
-
process_loaded_project_config,
|
|
33
|
-
validate_federation_in_project_config,
|
|
34
|
-
)
|
|
28
|
+
from flwr.cli.config_migration import migrate, warn_if_federation_config_overrides
|
|
29
|
+
from flwr.cli.config_utils import load_and_validate
|
|
35
30
|
from flwr.cli.constant import FEDERATION_CONFIG_HELP_MESSAGE, RUN_CONFIG_HELP_MESSAGE
|
|
31
|
+
from flwr.cli.flower_config import (
|
|
32
|
+
_serialize_simulation_options,
|
|
33
|
+
read_superlink_connection,
|
|
34
|
+
)
|
|
35
|
+
from flwr.cli.typing import SuperLinkConnection, SuperLinkSimulationOptions
|
|
36
36
|
from flwr.common.config import (
|
|
37
37
|
flatten_dict,
|
|
38
38
|
get_metadata_from_config,
|
|
@@ -40,19 +40,18 @@ from flwr.common.config import (
|
|
|
40
40
|
user_config_to_configrecord,
|
|
41
41
|
)
|
|
42
42
|
from flwr.common.constant import FAB_CONFIG_FILE, CliOutputFormat
|
|
43
|
-
from flwr.common.logger import print_json_error, redirect_output, restore_output
|
|
44
43
|
from flwr.common.serde import config_record_to_proto, fab_to_proto, user_config_to_proto
|
|
45
44
|
from flwr.common.typing import Fab
|
|
46
45
|
from flwr.proto.control_pb2 import StartRunRequest # pylint: disable=E0611
|
|
47
46
|
from flwr.proto.control_pb2_grpc import ControlStub
|
|
48
|
-
from flwr.supercore.
|
|
47
|
+
from flwr.supercore.utils import check_federation_format, parse_app_spec
|
|
49
48
|
|
|
50
49
|
from ..log import start_stream
|
|
51
50
|
from ..utils import (
|
|
51
|
+
cli_output_handler,
|
|
52
52
|
flwr_cli_grpc_exc_handler,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
parse_app_spec,
|
|
53
|
+
init_channel_from_connection,
|
|
54
|
+
print_json_to_stdout,
|
|
56
55
|
)
|
|
57
56
|
|
|
58
57
|
CONN_REFRESH_PERIOD = 60 # Connection refresh period for log streaming (seconds)
|
|
@@ -64,9 +63,17 @@ def run(
|
|
|
64
63
|
Path,
|
|
65
64
|
typer.Argument(help="Path of the Flower App to run."),
|
|
66
65
|
] = Path("."),
|
|
66
|
+
superlink: Annotated[
|
|
67
|
+
str | None,
|
|
68
|
+
typer.Argument(help="Name of the SuperLink connection."),
|
|
69
|
+
] = None,
|
|
67
70
|
federation: Annotated[
|
|
68
71
|
str | None,
|
|
69
|
-
typer.
|
|
72
|
+
typer.Option(
|
|
73
|
+
"--federation",
|
|
74
|
+
help="The federation to submit the run to; must be in the "
|
|
75
|
+
"format `@<account>/<federation>`.",
|
|
76
|
+
),
|
|
70
77
|
] = None,
|
|
71
78
|
run_config_overrides: Annotated[
|
|
72
79
|
list[str] | None,
|
|
@@ -81,6 +88,7 @@ def run(
|
|
|
81
88
|
typer.Option(
|
|
82
89
|
"--federation-config",
|
|
83
90
|
help=FEDERATION_CONFIG_HELP_MESSAGE,
|
|
91
|
+
hidden=True,
|
|
84
92
|
),
|
|
85
93
|
] = None,
|
|
86
94
|
stream: Annotated[
|
|
@@ -101,88 +109,87 @@ def run(
|
|
|
101
109
|
] = CliOutputFormat.DEFAULT,
|
|
102
110
|
) -> None:
|
|
103
111
|
"""Run Flower App."""
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
112
|
+
with cli_output_handler(output_format=output_format) as is_json:
|
|
113
|
+
# Warn `--federation-config` is ignored
|
|
114
|
+
warn_if_federation_config_overrides(federation_config_overrides)
|
|
115
|
+
|
|
116
|
+
# Migrate legacy usage if any
|
|
117
|
+
migrate(str(app), [], ignore_legacy_usage=True)
|
|
118
|
+
|
|
119
|
+
# Read superlink connection configuration
|
|
120
|
+
superlink_connection = read_superlink_connection(superlink)
|
|
109
121
|
|
|
110
122
|
# Determine if app is remote
|
|
111
123
|
app_spec = None
|
|
124
|
+
config: dict[str, Any] = {}
|
|
112
125
|
if (app_str := str(app)).startswith("@"):
|
|
113
126
|
# Validate app version and ID format
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
127
|
+
try:
|
|
128
|
+
_ = parse_app_spec(app_str)
|
|
129
|
+
except ValueError as e:
|
|
130
|
+
raise click.ClickException(str(e)) from e
|
|
117
131
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
# Disable the validation for remote apps
|
|
121
|
-
pyproject_path = app / "pyproject.toml" if not is_remote_app else None
|
|
122
|
-
# `./pyproject.toml` will be loaded when `pyproject_path` is None
|
|
123
|
-
config, errors, warnings = load_and_validate(
|
|
124
|
-
pyproject_path, check_module=not is_remote_app
|
|
125
|
-
)
|
|
126
|
-
config = process_loaded_project_config(config, errors, warnings)
|
|
132
|
+
app_spec = app_str
|
|
127
133
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
134
|
+
# Validate TOML configuration for local app
|
|
135
|
+
else:
|
|
136
|
+
app = app.expanduser().resolve() # Resolve path to absolute
|
|
137
|
+
config, warnings = load_and_validate(app / FAB_CONFIG_FILE)
|
|
138
|
+
if warnings:
|
|
139
|
+
typer.secho(
|
|
140
|
+
f"Flower App configuration warnings in '{app / FAB_CONFIG_FILE}':\n"
|
|
141
|
+
+ "\n".join([f"- {line}" for line in warnings]),
|
|
142
|
+
fg=typer.colors.YELLOW,
|
|
143
|
+
bold=True,
|
|
144
|
+
)
|
|
131
145
|
|
|
132
|
-
if
|
|
146
|
+
if superlink_connection.address:
|
|
133
147
|
_run_with_control_api(
|
|
134
148
|
app,
|
|
149
|
+
config,
|
|
135
150
|
federation,
|
|
136
|
-
|
|
151
|
+
superlink_connection,
|
|
137
152
|
run_config_overrides,
|
|
138
153
|
stream,
|
|
139
|
-
|
|
154
|
+
is_json,
|
|
140
155
|
app_spec,
|
|
141
156
|
)
|
|
142
157
|
else:
|
|
143
158
|
_run_without_control_api(
|
|
144
|
-
app,
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if suppress_output:
|
|
148
|
-
restore_output()
|
|
149
|
-
e_message = captured_output.getvalue()
|
|
150
|
-
print_json_error(e_message, err)
|
|
151
|
-
else:
|
|
152
|
-
typer.secho(
|
|
153
|
-
f"{err}",
|
|
154
|
-
fg=typer.colors.RED,
|
|
155
|
-
bold=True,
|
|
156
|
-
err=True,
|
|
159
|
+
app=app,
|
|
160
|
+
simulation_options=superlink_connection.options, # type: ignore
|
|
161
|
+
config_overrides=run_config_overrides,
|
|
157
162
|
)
|
|
158
|
-
finally:
|
|
159
|
-
if suppress_output:
|
|
160
|
-
restore_output()
|
|
161
|
-
captured_output.close()
|
|
162
163
|
|
|
163
164
|
|
|
164
165
|
# pylint: disable-next=R0913, R0914, R0917
|
|
165
166
|
def _run_with_control_api(
|
|
166
167
|
app: Path,
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
config: dict[str, Any],
|
|
169
|
+
federation: str | None,
|
|
170
|
+
superlink_connection: SuperLinkConnection,
|
|
169
171
|
config_overrides: list[str] | None,
|
|
170
172
|
stream: bool,
|
|
171
|
-
|
|
173
|
+
is_json: bool,
|
|
172
174
|
app_spec: str | None,
|
|
173
175
|
) -> None:
|
|
174
176
|
channel = None
|
|
175
177
|
is_remote_app = app_spec is not None
|
|
178
|
+
|
|
179
|
+
# Determine federation to use
|
|
180
|
+
if federation: # Override federation from CLI
|
|
181
|
+
check_federation_format(federation)
|
|
182
|
+
else: # Use federation from SuperLink connection if set
|
|
183
|
+
federation = superlink_connection.federation or ""
|
|
184
|
+
|
|
176
185
|
try:
|
|
177
|
-
|
|
178
|
-
channel = init_channel(app, federation_config, auth_plugin)
|
|
186
|
+
channel = init_channel_from_connection(superlink_connection)
|
|
179
187
|
stub = ControlStub(channel)
|
|
180
188
|
|
|
181
189
|
# Build FAB if local app
|
|
182
190
|
if not is_remote_app:
|
|
183
191
|
fab_bytes = build_fab_from_disk(app)
|
|
184
192
|
fab_hash = hashlib.sha256(fab_bytes).hexdigest()
|
|
185
|
-
config = cast(dict[str, Any], load_toml(app / FAB_CONFIG_FILE))
|
|
186
193
|
fab_id, fab_version = get_metadata_from_config(config)
|
|
187
194
|
fab = Fab(fab_hash, fab_bytes, {})
|
|
188
195
|
# Skip FAB build if remote app
|
|
@@ -191,16 +198,19 @@ def _run_with_control_api(
|
|
|
191
198
|
fab_id = fab_version = fab_hash = ""
|
|
192
199
|
fab = Fab(fab_hash, b"", {})
|
|
193
200
|
|
|
194
|
-
real_federation: str = federation_config.get("federation", NOOP_FEDERATION)
|
|
195
|
-
|
|
196
201
|
# Construct a `ConfigRecord` out of a flattened `UserConfig`
|
|
197
|
-
|
|
198
|
-
|
|
202
|
+
options = {}
|
|
203
|
+
if superlink_connection.options:
|
|
204
|
+
options = flatten_dict(
|
|
205
|
+
_serialize_simulation_options(superlink_connection.options)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
c_record = user_config_to_configrecord(options)
|
|
199
209
|
|
|
200
210
|
req = StartRunRequest(
|
|
201
211
|
fab=fab_to_proto(fab),
|
|
202
212
|
override_config=user_config_to_proto(parse_config_args(config_overrides)),
|
|
203
|
-
federation=
|
|
213
|
+
federation=federation,
|
|
204
214
|
federation_options=config_record_to_proto(c_record),
|
|
205
215
|
app_spec=app_spec or "",
|
|
206
216
|
)
|
|
@@ -212,22 +222,13 @@ def _run_with_control_api(
|
|
|
212
222
|
f"🎊 Successfully started run {res.run_id}", fg=typer.colors.GREEN
|
|
213
223
|
)
|
|
214
224
|
else:
|
|
215
|
-
|
|
216
|
-
typer.secho(
|
|
217
|
-
"❌ Failed to start run. Please check that the provided "
|
|
218
|
-
"app identifier (@account_name/app_name) is correct.",
|
|
219
|
-
fg=typer.colors.RED,
|
|
220
|
-
err=True,
|
|
221
|
-
)
|
|
222
|
-
else:
|
|
223
|
-
typer.secho("❌ Failed to start run", fg=typer.colors.RED, err=True)
|
|
224
|
-
raise typer.Exit(code=1)
|
|
225
|
+
raise click.ClickException("Failed to start run")
|
|
225
226
|
|
|
226
|
-
if
|
|
227
|
+
if is_json:
|
|
227
228
|
# Only include FAB metadata if we actually built a local FAB
|
|
228
229
|
payload: dict[str, Any] = {
|
|
229
230
|
"success": res.HasField("run_id"),
|
|
230
|
-
"run-id": res.run_id if res.HasField("run_id") else None,
|
|
231
|
+
"run-id": f"{res.run_id}" if res.HasField("run_id") else None,
|
|
231
232
|
}
|
|
232
233
|
if not is_remote_app:
|
|
233
234
|
payload.update(
|
|
@@ -239,8 +240,7 @@ def _run_with_control_api(
|
|
|
239
240
|
"fab-filename": get_fab_filename(config, fab_hash),
|
|
240
241
|
}
|
|
241
242
|
)
|
|
242
|
-
|
|
243
|
-
Console().print_json(json.dumps(payload))
|
|
243
|
+
print_json_to_stdout(payload)
|
|
244
244
|
|
|
245
245
|
if stream:
|
|
246
246
|
start_stream(res.run_id, channel, CONN_REFRESH_PERIOD)
|
|
@@ -251,26 +251,12 @@ def _run_with_control_api(
|
|
|
251
251
|
|
|
252
252
|
def _run_without_control_api(
|
|
253
253
|
app: Path | None,
|
|
254
|
-
|
|
254
|
+
simulation_options: SuperLinkSimulationOptions,
|
|
255
255
|
config_overrides: list[str] | None,
|
|
256
|
-
federation: str,
|
|
257
256
|
) -> None:
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
backend_cfg = federation_config["options"].get("backend", {})
|
|
262
|
-
except KeyError as err:
|
|
263
|
-
typer.secho(
|
|
264
|
-
"❌ The project's `pyproject.toml` needs to declare the number of"
|
|
265
|
-
" SuperNodes in the simulation. To simulate 10 SuperNodes,"
|
|
266
|
-
" use the following notation:\n\n"
|
|
267
|
-
f"[tool.flwr.federations.{federation}]\n"
|
|
268
|
-
"options.num-supernodes = 10\n",
|
|
269
|
-
fg=typer.colors.RED,
|
|
270
|
-
bold=True,
|
|
271
|
-
err=True,
|
|
272
|
-
)
|
|
273
|
-
raise typer.Exit(code=1) from err
|
|
257
|
+
|
|
258
|
+
num_supernodes = simulation_options.num_supernodes
|
|
259
|
+
verbose = simulation_options.verbose or False
|
|
274
260
|
|
|
275
261
|
command = [
|
|
276
262
|
"flower-simulation",
|
|
@@ -280,9 +266,10 @@ def _run_without_control_api(
|
|
|
280
266
|
f"{num_supernodes}",
|
|
281
267
|
]
|
|
282
268
|
|
|
283
|
-
if
|
|
269
|
+
if simulation_options.backend:
|
|
284
270
|
# Stringify as JSON
|
|
285
|
-
|
|
271
|
+
backend_serial = _serialize_simulation_options(simulation_options)
|
|
272
|
+
command.extend(["--backend-config", json.dumps(backend_serial)])
|
|
286
273
|
|
|
287
274
|
if verbose:
|
|
288
275
|
command.extend(["--verbose"])
|
flwr/cli/run_utils.py
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
from dataclasses import dataclass
|
|
19
19
|
from datetime import datetime, timedelta
|
|
20
20
|
|
|
21
|
-
from flwr.common.date import format_timedelta, isoformat8601_utc
|
|
22
21
|
from flwr.common.typing import Run
|
|
22
|
+
from flwr.supercore.date import isoformat8601_utc
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
@dataclass
|
|
@@ -40,8 +40,8 @@ class RunRow: # pylint: disable=too-many-instance-attributes
|
|
|
40
40
|
The SHA-256 hash of the FAB.
|
|
41
41
|
status_text : str
|
|
42
42
|
The formatted status text.
|
|
43
|
-
elapsed :
|
|
44
|
-
The
|
|
43
|
+
elapsed : float
|
|
44
|
+
The elapsed time in seconds.
|
|
45
45
|
pending_at : str
|
|
46
46
|
Timestamp when run entered pending state.
|
|
47
47
|
starting_at : str
|
|
@@ -50,6 +50,16 @@ class RunRow: # pylint: disable=too-many-instance-attributes
|
|
|
50
50
|
Timestamp when run entered running state.
|
|
51
51
|
finished_at : str
|
|
52
52
|
Timestamp when run finished.
|
|
53
|
+
network_traffic_inbound : int
|
|
54
|
+
The total inbound network traffic (in bytes) used during the run.
|
|
55
|
+
It includes the traffic from SuperNodes to SuperLink.
|
|
56
|
+
network_traffic_outbound : int
|
|
57
|
+
The total outbound network traffic (in bytes) used during the run.
|
|
58
|
+
It includes the traffic from SuperLink to SuperNodes.
|
|
59
|
+
compute_time_serverapp : float
|
|
60
|
+
The total compute time (in seconds) of the ServerApp during the run.
|
|
61
|
+
compute_time_clientapp : float
|
|
62
|
+
The total compute time (in seconds) of all ClientApps during the run.
|
|
53
63
|
"""
|
|
54
64
|
|
|
55
65
|
run_id: int
|
|
@@ -58,11 +68,15 @@ class RunRow: # pylint: disable=too-many-instance-attributes
|
|
|
58
68
|
fab_version: str
|
|
59
69
|
fab_hash: str
|
|
60
70
|
status_text: str
|
|
61
|
-
elapsed:
|
|
71
|
+
elapsed: float
|
|
62
72
|
pending_at: str
|
|
63
73
|
starting_at: str
|
|
64
74
|
running_at: str
|
|
65
75
|
finished_at: str
|
|
76
|
+
network_traffic_inbound: int
|
|
77
|
+
network_traffic_outbound: int
|
|
78
|
+
compute_time_serverapp: float
|
|
79
|
+
compute_time_clientapp: float
|
|
66
80
|
|
|
67
81
|
|
|
68
82
|
def format_runs(runs: list[Run], now_isoformat: str) -> list[RunRow]:
|
|
@@ -120,11 +134,15 @@ def format_runs(runs: list[Run], now_isoformat: str) -> list[RunRow]:
|
|
|
120
134
|
fab_version=run.fab_version,
|
|
121
135
|
fab_hash=run.fab_hash,
|
|
122
136
|
status_text=status_text,
|
|
123
|
-
elapsed=
|
|
137
|
+
elapsed=elapsed_time.total_seconds(),
|
|
124
138
|
pending_at=_format_datetime(pending_at),
|
|
125
139
|
starting_at=_format_datetime(starting_at),
|
|
126
140
|
running_at=_format_datetime(running_at),
|
|
127
141
|
finished_at=_format_datetime(finished_at),
|
|
142
|
+
network_traffic_inbound=run.bytes_recv,
|
|
143
|
+
network_traffic_outbound=run.bytes_sent,
|
|
144
|
+
compute_time_serverapp=elapsed_time.total_seconds(),
|
|
145
|
+
compute_time_clientapp=run.clientapp_runtime,
|
|
128
146
|
)
|
|
129
147
|
run_list.append(row)
|
|
130
148
|
return run_list
|
flwr/cli/stop.py
CHANGED
|
@@ -15,50 +15,45 @@
|
|
|
15
15
|
"""Flower command line interface `stop` command."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
import io
|
|
19
|
-
import json
|
|
20
|
-
from pathlib import Path
|
|
21
18
|
from typing import Annotated
|
|
22
19
|
|
|
20
|
+
import click
|
|
23
21
|
import typer
|
|
24
|
-
from rich.console import Console
|
|
25
22
|
|
|
26
|
-
from flwr.cli.
|
|
27
|
-
exit_if_no_address,
|
|
28
|
-
load_and_validate,
|
|
29
|
-
process_loaded_project_config,
|
|
30
|
-
validate_federation_in_project_config,
|
|
31
|
-
)
|
|
23
|
+
from flwr.cli.config_migration import migrate, warn_if_federation_config_overrides
|
|
32
24
|
from flwr.cli.constant import FEDERATION_CONFIG_HELP_MESSAGE
|
|
33
|
-
from flwr.
|
|
34
|
-
from flwr.common.
|
|
25
|
+
from flwr.cli.flower_config import read_superlink_connection
|
|
26
|
+
from flwr.common.constant import CliOutputFormat
|
|
35
27
|
from flwr.proto.control_pb2 import ( # pylint: disable=E0611
|
|
36
28
|
StopRunRequest,
|
|
37
29
|
StopRunResponse,
|
|
38
30
|
)
|
|
39
31
|
from flwr.proto.control_pb2_grpc import ControlStub
|
|
40
32
|
|
|
41
|
-
from .utils import
|
|
33
|
+
from .utils import (
|
|
34
|
+
cli_output_handler,
|
|
35
|
+
flwr_cli_grpc_exc_handler,
|
|
36
|
+
init_channel_from_connection,
|
|
37
|
+
print_json_to_stdout,
|
|
38
|
+
)
|
|
42
39
|
|
|
43
40
|
|
|
44
41
|
def stop( # pylint: disable=R0914
|
|
42
|
+
ctx: typer.Context,
|
|
45
43
|
run_id: Annotated[ # pylint: disable=unused-argument
|
|
46
44
|
int,
|
|
47
45
|
typer.Argument(help="The Flower run ID to stop"),
|
|
48
46
|
],
|
|
49
|
-
|
|
50
|
-
Path,
|
|
51
|
-
typer.Argument(help="Path of the Flower project"),
|
|
52
|
-
] = Path("."),
|
|
53
|
-
federation: Annotated[
|
|
47
|
+
superlink: Annotated[
|
|
54
48
|
str | None,
|
|
55
|
-
typer.Argument(help="Name of the
|
|
49
|
+
typer.Argument(help="Name of the SuperLink connection."),
|
|
56
50
|
] = None,
|
|
57
51
|
federation_config_overrides: Annotated[
|
|
58
52
|
list[str] | None,
|
|
59
53
|
typer.Option(
|
|
60
54
|
"--federation-config",
|
|
61
55
|
help=FEDERATION_CONFIG_HELP_MESSAGE,
|
|
56
|
+
hidden=True,
|
|
62
57
|
),
|
|
63
58
|
] = None,
|
|
64
59
|
output_format: Annotated[
|
|
@@ -75,61 +70,29 @@ def stop( # pylint: disable=R0914
|
|
|
75
70
|
This command stops a running Flower App execution by sending a stop request to the
|
|
76
71
|
SuperLink via the Control API.
|
|
77
72
|
"""
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
#
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
pyproject_path = app / FAB_CONFIG_FILE if app else None
|
|
88
|
-
config, errors, warnings = load_and_validate(pyproject_path, check_module=False)
|
|
89
|
-
config = process_loaded_project_config(config, errors, warnings)
|
|
90
|
-
federation, federation_config = validate_federation_in_project_config(
|
|
91
|
-
federation, config, federation_config_overrides
|
|
92
|
-
)
|
|
93
|
-
exit_if_no_address(federation_config, "stop")
|
|
73
|
+
with cli_output_handler(output_format=output_format) as is_json:
|
|
74
|
+
# Warn `--federation-config` is ignored
|
|
75
|
+
warn_if_federation_config_overrides(federation_config_overrides)
|
|
76
|
+
|
|
77
|
+
migrate(superlink, args=ctx.args)
|
|
78
|
+
|
|
79
|
+
# Read superlink connection configuration
|
|
80
|
+
superlink_connection = read_superlink_connection(superlink)
|
|
94
81
|
channel = None
|
|
82
|
+
|
|
95
83
|
try:
|
|
96
|
-
|
|
97
|
-
channel = init_channel(app, federation_config, auth_plugin)
|
|
84
|
+
channel = init_channel_from_connection(superlink_connection)
|
|
98
85
|
stub = ControlStub(channel) # pylint: disable=unused-variable # noqa: F841
|
|
99
86
|
|
|
100
87
|
typer.secho(f"✋ Stopping run ID {run_id}...", fg=typer.colors.GREEN)
|
|
101
|
-
_stop_run(stub=stub, run_id=run_id,
|
|
102
|
-
|
|
103
|
-
except ValueError as err:
|
|
104
|
-
typer.secho(
|
|
105
|
-
f"❌ {err}",
|
|
106
|
-
fg=typer.colors.RED,
|
|
107
|
-
bold=True,
|
|
108
|
-
err=True,
|
|
109
|
-
)
|
|
110
|
-
raise typer.Exit(code=1) from err
|
|
88
|
+
_stop_run(stub=stub, run_id=run_id, is_json=is_json)
|
|
89
|
+
|
|
111
90
|
finally:
|
|
112
91
|
if channel:
|
|
113
92
|
channel.close()
|
|
114
|
-
except (typer.Exit, Exception) as err: # pylint: disable=broad-except
|
|
115
|
-
if suppress_output:
|
|
116
|
-
restore_output()
|
|
117
|
-
e_message = captured_output.getvalue()
|
|
118
|
-
print_json_error(e_message, err)
|
|
119
|
-
else:
|
|
120
|
-
typer.secho(
|
|
121
|
-
f"{err}",
|
|
122
|
-
fg=typer.colors.RED,
|
|
123
|
-
bold=True,
|
|
124
|
-
err=True,
|
|
125
|
-
)
|
|
126
|
-
finally:
|
|
127
|
-
if suppress_output:
|
|
128
|
-
restore_output()
|
|
129
|
-
captured_output.close()
|
|
130
93
|
|
|
131
94
|
|
|
132
|
-
def _stop_run(stub: ControlStub, run_id: int,
|
|
95
|
+
def _stop_run(stub: ControlStub, run_id: int, is_json: bool) -> None:
|
|
133
96
|
"""Stop a run and display the result.
|
|
134
97
|
|
|
135
98
|
Parameters
|
|
@@ -138,23 +101,19 @@ def _stop_run(stub: ControlStub, run_id: int, output_format: str) -> None:
|
|
|
138
101
|
The gRPC stub for Control API communication.
|
|
139
102
|
run_id : int
|
|
140
103
|
The unique identifier of the run to stop.
|
|
141
|
-
|
|
142
|
-
|
|
104
|
+
is_json : bool
|
|
105
|
+
Whether JSON output format is requested.
|
|
143
106
|
"""
|
|
144
107
|
with flwr_cli_grpc_exc_handler():
|
|
145
108
|
response: StopRunResponse = stub.StopRun(request=StopRunRequest(run_id=run_id))
|
|
146
109
|
if response.success:
|
|
147
110
|
typer.secho(f"✅ Run {run_id} successfully stopped.", fg=typer.colors.GREEN)
|
|
148
|
-
if
|
|
149
|
-
|
|
111
|
+
if is_json:
|
|
112
|
+
print_json_to_stdout(
|
|
150
113
|
{
|
|
151
114
|
"success": True,
|
|
152
|
-
"run-id": run_id,
|
|
115
|
+
"run-id": f"{run_id}",
|
|
153
116
|
}
|
|
154
117
|
)
|
|
155
|
-
restore_output()
|
|
156
|
-
Console().print_json(run_output)
|
|
157
118
|
else:
|
|
158
|
-
|
|
159
|
-
f"❌ Run {run_id} couldn't be stopped.", fg=typer.colors.RED, err=True
|
|
160
|
-
)
|
|
119
|
+
raise click.ClickException(f"Run {run_id} couldn't be stopped.")
|