flwr 1.25.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 +18 -69
- 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 +28 -58
- flwr/cli/new/new.py +9 -29
- flwr/cli/pull.py +19 -37
- flwr/cli/run/run.py +85 -93
- flwr/cli/run_utils.py +1 -1
- flwr/cli/stop.py +32 -73
- flwr/cli/supernode/ls.py +25 -57
- 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 -275
- flwr/client/grpc_rere_client/connection.py +3 -3
- 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/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 +7 -5
- flwr/common/telemetry.py +1 -1
- flwr/common/typing.py +4 -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/run_pb2.py +5 -13
- flwr/proto/run_pb2.pyi +0 -57
- 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 +6 -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 +6 -5
- flwr/server/superlink/linkstate/__init__.py +2 -2
- flwr/server/superlink/linkstate/in_memory_linkstate.py +2 -10
- flwr/server/superlink/linkstate/linkstate.py +2 -21
- flwr/server/superlink/linkstate/linkstate_factory.py +16 -8
- flwr/server/superlink/linkstate/{sqlite_linkstate.py → sql_linkstate.py} +432 -534
- 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 +24 -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/supercore/state/__init__.py +15 -0
- flwr/supercore/state/alembic/__init__.py +15 -0
- 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/supercore/state/alembic/versions/__init__.py +15 -0
- 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/supercore/state/schema/__init__.py +15 -0
- 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 +36 -1
- flwr/superlink/federation/federation_manager.py +2 -2
- flwr/superlink/federation/noop_federation_manager.py +8 -6
- flwr/superlink/servicer/control/control_servicer.py +19 -17
- flwr/supernode/cli/flower_supernode.py +2 -1
- flwr/supernode/runtime/run_clientapp.py +14 -14
- flwr/supernode/servicer/clientappio/clientappio_servicer.py +10 -8
- flwr/supernode/start_client_internal.py +10 -6
- {flwr-1.25.0.dist-info → flwr-1.26.0.dist-info}/METADATA +7 -5
- {flwr-1.25.0.dist-info → flwr-1.26.0.dist-info}/RECORD +137 -116
- flwr/cli/federation/show.py +0 -318
- flwr/common/pyproject.py +0 -42
- flwr/supercore/sqlite_mixin.py +0 -159
- /flwr/{common → supercore}/version.py +0 -0
- {flwr-1.25.0.dist-info → flwr-1.26.0.dist-info}/WHEEL +0 -0
- {flwr-1.25.0.dist-info → flwr-1.26.0.dist-info}/entry_points.txt +0 -0
flwr/client/run_info_store.py
CHANGED
|
@@ -18,13 +18,14 @@
|
|
|
18
18
|
from dataclasses import dataclass
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
|
|
21
|
+
from flwr.app.user_config import UserConfig
|
|
21
22
|
from flwr.common import Context, RecordDict
|
|
22
23
|
from flwr.common.config import (
|
|
23
24
|
get_fused_config,
|
|
24
25
|
get_fused_config_from_dir,
|
|
25
26
|
get_fused_config_from_fab,
|
|
26
27
|
)
|
|
27
|
-
from flwr.common.typing import Fab, Run
|
|
28
|
+
from flwr.common.typing import Fab, Run
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
@dataclass()
|
flwr/clientapp/client_app.py
CHANGED
|
@@ -19,6 +19,7 @@ import inspect
|
|
|
19
19
|
from collections.abc import Callable, Iterator
|
|
20
20
|
from contextlib import contextmanager
|
|
21
21
|
|
|
22
|
+
from flwr.app.message_type import MessageType
|
|
22
23
|
from flwr.app.metadata import validate_message_type
|
|
23
24
|
from flwr.client.client import Client
|
|
24
25
|
from flwr.client.message_handler.message_handler import (
|
|
@@ -26,7 +27,7 @@ from flwr.client.message_handler.message_handler import (
|
|
|
26
27
|
)
|
|
27
28
|
from flwr.client.mod.utils import make_ffn
|
|
28
29
|
from flwr.client.typing import ClientFnExt, Mod
|
|
29
|
-
from flwr.common import Context, Message
|
|
30
|
+
from flwr.common import Context, Message
|
|
30
31
|
from flwr.common.logger import warn_deprecated_feature
|
|
31
32
|
|
|
32
33
|
from .typing import ClientAppCallable
|
flwr/common/__init__.py
CHANGED
|
@@ -15,12 +15,13 @@
|
|
|
15
15
|
"""Common components shared between server and client."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
from flwr.app.message_type import MessageType as MessageType
|
|
19
|
+
|
|
18
20
|
from ..app.error import Error as Error
|
|
19
21
|
from ..app.metadata import Metadata as Metadata
|
|
20
|
-
from .
|
|
22
|
+
from ..supercore.date import now as now
|
|
21
23
|
from .constant import MessageTypeLegacy as MessageTypeLegacy
|
|
22
24
|
from .context import Context as Context
|
|
23
|
-
from .date import now as now
|
|
24
25
|
from .grpc import GRPC_MAX_MESSAGE_LENGTH
|
|
25
26
|
from .logger import configure as configure
|
|
26
27
|
from .logger import log as log
|
flwr/common/args.py
CHANGED
|
@@ -89,13 +89,13 @@ def try_obtain_root_certificates(
|
|
|
89
89
|
else:
|
|
90
90
|
# Load the certificates if provided, or load the system certificates
|
|
91
91
|
if root_cert_path is None:
|
|
92
|
-
log(INFO, "Using system certificates")
|
|
92
|
+
log(INFO, "Using system certificates for TLS connection")
|
|
93
93
|
root_certificates = None
|
|
94
94
|
elif not isfile(root_cert_path):
|
|
95
95
|
log(ERROR, "Path argument `--root-certificates` does not point to a file.")
|
|
96
96
|
sys.exit(1)
|
|
97
97
|
else:
|
|
98
|
-
root_certificates = Path(root_cert_path).read_bytes()
|
|
98
|
+
root_certificates = Path(root_cert_path).expanduser().read_bytes()
|
|
99
99
|
log(
|
|
100
100
|
DEBUG,
|
|
101
101
|
"Starting secure HTTPS channel to %s "
|
|
@@ -129,9 +129,9 @@ def try_obtain_server_certificates(
|
|
|
129
129
|
if not isfile(args.ssl_keyfile):
|
|
130
130
|
sys.exit("Path argument `--ssl-keyfile` does not point to a file.")
|
|
131
131
|
certificates = (
|
|
132
|
-
Path(args.ssl_ca_certfile).read_bytes(), # CA certificate
|
|
133
|
-
Path(args.ssl_certfile).read_bytes(), # server certificate
|
|
134
|
-
Path(args.ssl_keyfile).read_bytes(), # server private key
|
|
132
|
+
Path(args.ssl_ca_certfile).expanduser().read_bytes(), # CA certificate
|
|
133
|
+
Path(args.ssl_certfile).expanduser().read_bytes(), # server certificate
|
|
134
|
+
Path(args.ssl_keyfile).expanduser().read_bytes(), # server private key
|
|
135
135
|
)
|
|
136
136
|
return certificates
|
|
137
137
|
if args.ssl_certfile or args.ssl_keyfile or args.ssl_ca_certfile:
|
flwr/common/config.py
CHANGED
|
@@ -22,9 +22,10 @@ from io import BytesIO
|
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
from typing import IO, Any, TypeVar, cast, get_args
|
|
24
24
|
|
|
25
|
+
import click
|
|
25
26
|
import tomli
|
|
26
|
-
import typer
|
|
27
27
|
|
|
28
|
+
from flwr.app.user_config import UserConfig, UserConfigValue
|
|
28
29
|
from flwr.common.constant import (
|
|
29
30
|
APP_DIR,
|
|
30
31
|
FAB_CONFIG_FILE,
|
|
@@ -32,7 +33,7 @@ from flwr.common.constant import (
|
|
|
32
33
|
FLWR_DIR,
|
|
33
34
|
FLWR_HOME,
|
|
34
35
|
)
|
|
35
|
-
from flwr.common.typing import Run
|
|
36
|
+
from flwr.common.typing import Run
|
|
36
37
|
|
|
37
38
|
from . import ConfigRecord, object_ref
|
|
38
39
|
|
|
@@ -41,7 +42,7 @@ T_dict = TypeVar("T_dict", bound=dict[str, Any]) # pylint: disable=invalid-name
|
|
|
41
42
|
|
|
42
43
|
def get_flwr_dir(provided_path: str | None = None) -> Path:
|
|
43
44
|
"""Return the Flower home directory based on env variables."""
|
|
44
|
-
if provided_path is None or not Path(provided_path).is_dir():
|
|
45
|
+
if provided_path is None or not Path(provided_path).expanduser().is_dir():
|
|
45
46
|
return Path(
|
|
46
47
|
os.getenv(
|
|
47
48
|
FLWR_HOME,
|
|
@@ -212,7 +213,7 @@ def parse_config_args(config: list[str] | None, flatten: bool = True) -> dict[st
|
|
|
212
213
|
|
|
213
214
|
# Handle if .toml file is passed
|
|
214
215
|
if len(config) == 1 and config[0].endswith(".toml"):
|
|
215
|
-
with Path(config[0]).open("rb") as config_file:
|
|
216
|
+
with Path(config[0]).expanduser().open("rb") as config_file:
|
|
216
217
|
overrides = flatten_dict(tomli.load(config_file))
|
|
217
218
|
return overrides
|
|
218
219
|
|
|
@@ -234,17 +235,13 @@ def parse_config_args(config: list[str] | None, flatten: bool = True) -> dict[st
|
|
|
234
235
|
overrides.update(tomli.loads(toml_str))
|
|
235
236
|
flat_overrides = flatten_dict(overrides) if flatten else overrides
|
|
236
237
|
except tomli.TOMLDecodeError as err:
|
|
237
|
-
|
|
238
|
-
"
|
|
238
|
+
raise click.ClickException(
|
|
239
|
+
"The provided configuration string is in an invalid format. "
|
|
239
240
|
"The correct format should be, e.g., 'key1=123 key2=false "
|
|
240
241
|
'key3="string"\', where values must be of type bool, int, '
|
|
241
242
|
"string, or float. Ensure proper formatting with "
|
|
242
|
-
"space-separated key-value pairs."
|
|
243
|
-
|
|
244
|
-
bold=True,
|
|
245
|
-
err=True,
|
|
246
|
-
)
|
|
247
|
-
raise typer.Exit(code=1) from err
|
|
243
|
+
"space-separated key-value pairs."
|
|
244
|
+
) from err
|
|
248
245
|
|
|
249
246
|
return flat_overrides
|
|
250
247
|
|
|
@@ -333,8 +330,6 @@ def validate_fields_in_config(
|
|
|
333
330
|
warnings.append('Recommended property "description" missing in [project]')
|
|
334
331
|
if "license" not in config["project"]:
|
|
335
332
|
warnings.append('Recommended property "license" missing in [project]')
|
|
336
|
-
if "authors" not in config["project"]:
|
|
337
|
-
warnings.append('Recommended property "authors" missing in [project]')
|
|
338
333
|
|
|
339
334
|
if (
|
|
340
335
|
"tool" not in config
|
|
@@ -378,13 +373,13 @@ def validate_config(
|
|
|
378
373
|
is_valid, reason = object_ref.validate(serverapp_ref, check_module, project_dir)
|
|
379
374
|
|
|
380
375
|
if not is_valid and isinstance(reason, str):
|
|
381
|
-
return False, [reason],
|
|
376
|
+
return False, [reason], warnings
|
|
382
377
|
|
|
383
378
|
# Validate clientapp
|
|
384
379
|
clientapp_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
|
|
385
380
|
is_valid, reason = object_ref.validate(clientapp_ref, check_module, project_dir)
|
|
386
381
|
|
|
387
382
|
if not is_valid and isinstance(reason, str):
|
|
388
|
-
return False, [reason],
|
|
383
|
+
return False, [reason], warnings
|
|
389
384
|
|
|
390
|
-
return True, [],
|
|
385
|
+
return True, [], warnings
|
flwr/common/constant.py
CHANGED
|
@@ -19,13 +19,11 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
import os
|
|
21
21
|
|
|
22
|
-
TRANSPORT_TYPE_GRPC_BIDI = "grpc-bidi"
|
|
23
22
|
TRANSPORT_TYPE_GRPC_RERE = "grpc-rere"
|
|
24
23
|
TRANSPORT_TYPE_GRPC_ADAPTER = "grpc-adapter"
|
|
25
24
|
TRANSPORT_TYPE_REST = "rest"
|
|
26
25
|
TRANSPORT_TYPE_VCE = "vce"
|
|
27
26
|
TRANSPORT_TYPES = [
|
|
28
|
-
TRANSPORT_TYPE_GRPC_BIDI,
|
|
29
27
|
TRANSPORT_TYPE_GRPC_RERE,
|
|
30
28
|
TRANSPORT_TYPE_REST,
|
|
31
29
|
TRANSPORT_TYPE_VCE,
|
|
@@ -182,19 +180,8 @@ SUPERNODE_NOT_CREATED_FROM_CLI_MESSAGE = "Invalid SuperNode credentials"
|
|
|
182
180
|
PUBLIC_KEY_ALREADY_IN_USE_MESSAGE = "Public key already in use"
|
|
183
181
|
PUBLIC_KEY_NOT_VALID = "The provided public key is not valid"
|
|
184
182
|
NODE_NOT_FOUND_MESSAGE = "Node ID not found for account"
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
class MessageType:
|
|
188
|
-
"""Message type."""
|
|
189
|
-
|
|
190
|
-
TRAIN = "train"
|
|
191
|
-
EVALUATE = "evaluate"
|
|
192
|
-
QUERY = "query"
|
|
193
|
-
SYSTEM = "system"
|
|
194
|
-
|
|
195
|
-
def __new__(cls) -> MessageType:
|
|
196
|
-
"""Prevent instantiation."""
|
|
197
|
-
raise TypeError(f"{cls.__name__} cannot be instantiated.")
|
|
183
|
+
FEDERATION_NOT_SPECIFIED_MESSAGE = "No federation specified in the request"
|
|
184
|
+
FEDERATION_NOT_FOUND_MESSAGE = "Federation '%s' does not exist"
|
|
198
185
|
|
|
199
186
|
|
|
200
187
|
class MessageTypeLegacy:
|
|
@@ -323,4 +310,4 @@ class ExecPluginType:
|
|
|
323
310
|
|
|
324
311
|
# Constants for No-op auth plugins
|
|
325
312
|
NOOP_FLWR_AID = "<id:none>"
|
|
326
|
-
NOOP_ACCOUNT_NAME = "
|
|
313
|
+
NOOP_ACCOUNT_NAME = "none"
|
flwr/common/context.py
CHANGED
flwr/common/exit/exit.py
CHANGED
|
@@ -23,8 +23,8 @@ from logging import ERROR, INFO
|
|
|
23
23
|
from typing import Any, NoReturn
|
|
24
24
|
|
|
25
25
|
from flwr.common import EventType, event
|
|
26
|
-
from flwr.common.version import package_version
|
|
27
26
|
from flwr.supercore.constant import FORCE_EXIT_TIMEOUT_SECONDS
|
|
27
|
+
from flwr.supercore.version import package_version
|
|
28
28
|
|
|
29
29
|
from ..logger import log
|
|
30
30
|
from .exit_code import EXIT_CODE_HELP
|
|
@@ -87,9 +87,6 @@ def flwr_exit(
|
|
|
87
87
|
# Log the exit message
|
|
88
88
|
log(log_level, exit_message)
|
|
89
89
|
|
|
90
|
-
# Trigger exit handlers
|
|
91
|
-
trigger_exit_handlers()
|
|
92
|
-
|
|
93
90
|
# Start a daemon thread to force exit if graceful exit fails
|
|
94
91
|
def force_exit() -> None:
|
|
95
92
|
time.sleep(FORCE_EXIT_TIMEOUT_SECONDS)
|
|
@@ -97,6 +94,9 @@ def flwr_exit(
|
|
|
97
94
|
|
|
98
95
|
threading.Thread(target=force_exit, daemon=True).start()
|
|
99
96
|
|
|
97
|
+
# Trigger exit handlers
|
|
98
|
+
trigger_exit_handlers()
|
|
99
|
+
|
|
100
100
|
# Exit
|
|
101
101
|
sys.exit(sys_exit_code)
|
|
102
102
|
|
flwr/common/exit/exit_code.py
CHANGED
|
@@ -33,6 +33,7 @@ class ExitCode:
|
|
|
33
33
|
SUPERLINK_LICENSE_MISSING = 102
|
|
34
34
|
SUPERLINK_LICENSE_URL_INVALID = 103
|
|
35
35
|
SUPERLINK_INVALID_ARGS = 104
|
|
36
|
+
SUPERLINK_DATABASE_SCHEMA_MISMATCH = 105
|
|
36
37
|
|
|
37
38
|
# ServerApp-specific exit codes (200-299)
|
|
38
39
|
SERVERAPP_STRATEGY_PRECONDITION_UNMET = 200
|
|
@@ -91,6 +92,11 @@ EXIT_CODE_HELP = {
|
|
|
91
92
|
"Invalid arguments provided to SuperLink. Use `--help` check for the correct "
|
|
92
93
|
"usage. Alternatively, check the documentation."
|
|
93
94
|
),
|
|
95
|
+
ExitCode.SUPERLINK_DATABASE_SCHEMA_MISMATCH: (
|
|
96
|
+
"The database schema does not match the expected schema for this version of "
|
|
97
|
+
"SuperLink. Please refer to the documentation for guidance on how to resolve "
|
|
98
|
+
"this issue."
|
|
99
|
+
),
|
|
94
100
|
# ServerApp-specific exit codes (200-299)
|
|
95
101
|
ExitCode.SERVERAPP_STRATEGY_PRECONDITION_UNMET: (
|
|
96
102
|
"The strategy received replies that cannot be aggregated. Please ensure all "
|
flwr/common/grpc.py
CHANGED
flwr/common/logger.py
CHANGED
|
@@ -385,7 +385,7 @@ def start_log_uploader(
|
|
|
385
385
|
) -> threading.Thread:
|
|
386
386
|
"""Start the log uploader thread and return it."""
|
|
387
387
|
thread = threading.Thread(
|
|
388
|
-
target=_log_uploader, args=(log_queue, node_id, run_id, stub)
|
|
388
|
+
target=_log_uploader, args=(log_queue, node_id, run_id, stub), daemon=True
|
|
389
389
|
)
|
|
390
390
|
thread.start()
|
|
391
391
|
return thread
|
flwr/common/message.py
CHANGED
|
@@ -20,11 +20,11 @@ from __future__ import annotations
|
|
|
20
20
|
from logging import WARNING
|
|
21
21
|
from typing import Any, cast, overload
|
|
22
22
|
|
|
23
|
-
from flwr.common.date import now
|
|
24
23
|
from flwr.common.logger import warn_deprecated_feature
|
|
25
24
|
from flwr.proto.message_pb2 import Message as ProtoMessage # pylint: disable=E0611
|
|
26
25
|
from flwr.proto.message_pb2 import Metadata as ProtoMetadata # pylint: disable=E0611
|
|
27
26
|
from flwr.proto.message_pb2 import ObjectIDs # pylint: disable=E0611
|
|
27
|
+
from flwr.supercore.date import now
|
|
28
28
|
|
|
29
29
|
from ..app.error import Error
|
|
30
30
|
from ..app.metadata import Metadata
|
flwr/common/retry_invoker.py
CHANGED
|
@@ -21,7 +21,7 @@ import threading
|
|
|
21
21
|
import time
|
|
22
22
|
from collections.abc import Callable, Generator, Iterable
|
|
23
23
|
from dataclasses import dataclass
|
|
24
|
-
from logging import INFO, WARN
|
|
24
|
+
from logging import DEBUG, ERROR, INFO, WARN
|
|
25
25
|
from typing import Any, cast
|
|
26
26
|
|
|
27
27
|
import grpc
|
|
@@ -318,7 +318,7 @@ class RetryInvoker:
|
|
|
318
318
|
return ret
|
|
319
319
|
|
|
320
320
|
|
|
321
|
-
def
|
|
321
|
+
def make_simple_grpc_retry_invoker() -> RetryInvoker:
|
|
322
322
|
"""Create a simple gRPC retry invoker."""
|
|
323
323
|
lock = threading.Lock()
|
|
324
324
|
system_healthy = threading.Event()
|
|
@@ -334,8 +334,11 @@ def _make_simple_grpc_retry_invoker() -> RetryInvoker:
|
|
|
334
334
|
retry_state.tries,
|
|
335
335
|
)
|
|
336
336
|
|
|
337
|
-
def _on_backoff(
|
|
337
|
+
def _on_backoff(retry_state: RetryState) -> None:
|
|
338
338
|
system_healthy.clear()
|
|
339
|
+
log(
|
|
340
|
+
DEBUG, "Connection attempt failed with exception: %s", retry_state.exception
|
|
341
|
+
)
|
|
339
342
|
|
|
340
343
|
def _on_giveup(retry_state: RetryState) -> None:
|
|
341
344
|
system_healthy.clear()
|
|
@@ -351,7 +354,12 @@ def _make_simple_grpc_retry_invoker() -> RetryInvoker:
|
|
|
351
354
|
if e.code() == grpc.StatusCode.PERMISSION_DENIED: # type: ignore
|
|
352
355
|
raise RunNotRunningException
|
|
353
356
|
if e.code() == grpc.StatusCode.UNAVAILABLE: # type: ignore
|
|
354
|
-
|
|
357
|
+
# Check if this is an SSL handshake failure - these should fail fast
|
|
358
|
+
details = str(e.details() if hasattr(e, "details") else "").lower()
|
|
359
|
+
if "handshake failed" in details:
|
|
360
|
+
log(ERROR, "SSL/TLS handshake error detected.")
|
|
361
|
+
return True # Give up on SSL/TLS handshake errors
|
|
362
|
+
return False # Retry on other UNAVAILABLE errors (network issues)
|
|
355
363
|
return True
|
|
356
364
|
|
|
357
365
|
def _wait(wait_time: float) -> None:
|
|
@@ -386,7 +394,7 @@ def _make_simple_grpc_retry_invoker() -> RetryInvoker:
|
|
|
386
394
|
)
|
|
387
395
|
|
|
388
396
|
|
|
389
|
-
def
|
|
397
|
+
def wrap_stub(
|
|
390
398
|
stub: (
|
|
391
399
|
ServerAppIoStub | ClientAppIoStub | SimulationIoStub | FleetStub | GrpcAdapter
|
|
392
400
|
),
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
from typing import Any
|
|
19
19
|
|
|
20
20
|
import numpy as np
|
|
21
|
-
from numpy.typing import
|
|
21
|
+
from numpy.typing import NDArray
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def factor_combine(factor: int, parameters: list[NDArray[Any]]) -> list[NDArray[Any]]:
|
|
@@ -38,8 +38,11 @@ def get_parameters_shape(parameters: list[NDArray[Any]]) -> list[tuple[int, ...]
|
|
|
38
38
|
return [arr.shape for arr in parameters]
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
default_numpy_dtype = np.dtype(np.int64)
|
|
42
|
+
|
|
43
|
+
|
|
41
44
|
def get_zero_parameters(
|
|
42
|
-
dimensions_list: list[tuple[int, ...]], dtype:
|
|
45
|
+
dimensions_list: list[tuple[int, ...]], dtype: np.dtype[Any] = default_numpy_dtype
|
|
43
46
|
) -> list[NDArray[Any]]:
|
|
44
47
|
"""Generate zero parameters based on the dimensions list."""
|
|
45
48
|
return [np.zeros(dimensions, dtype=dtype) for dimensions in dimensions_list]
|
flwr/common/serde.py
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
from typing import Any, cast
|
|
19
19
|
|
|
20
|
+
from flwr.app.user_config import UserConfig, UserConfigValue
|
|
21
|
+
|
|
20
22
|
# pylint: disable=E0611
|
|
21
23
|
from flwr.proto.fab_pb2 import Fab as ProtoFab
|
|
22
24
|
from flwr.proto.message_pb2 import Context as ProtoContext
|
|
@@ -513,7 +515,7 @@ def fab_from_proto(fab: ProtoFab) -> typing.Fab:
|
|
|
513
515
|
# === User configs ===
|
|
514
516
|
|
|
515
517
|
|
|
516
|
-
def user_config_to_proto(user_config:
|
|
518
|
+
def user_config_to_proto(user_config: UserConfig) -> Any:
|
|
517
519
|
"""Serialize `UserConfig` to ProtoBuf."""
|
|
518
520
|
proto = {}
|
|
519
521
|
for key, value in user_config.items():
|
|
@@ -521,7 +523,7 @@ def user_config_to_proto(user_config: typing.UserConfig) -> Any:
|
|
|
521
523
|
return proto
|
|
522
524
|
|
|
523
525
|
|
|
524
|
-
def user_config_from_proto(proto: Any) ->
|
|
526
|
+
def user_config_from_proto(proto: Any) -> UserConfig:
|
|
525
527
|
"""Deserialize `UserConfig` from ProtoBuf."""
|
|
526
528
|
metrics = {}
|
|
527
529
|
for key, value in proto.items():
|
|
@@ -529,7 +531,7 @@ def user_config_from_proto(proto: Any) -> typing.UserConfig:
|
|
|
529
531
|
return metrics
|
|
530
532
|
|
|
531
533
|
|
|
532
|
-
def user_config_value_to_proto(user_config_value:
|
|
534
|
+
def user_config_value_to_proto(user_config_value: UserConfigValue) -> Scalar:
|
|
533
535
|
"""Serialize `UserConfigValue` to ProtoBuf."""
|
|
534
536
|
if isinstance(user_config_value, bool):
|
|
535
537
|
return Scalar(bool=user_config_value)
|
|
@@ -548,11 +550,11 @@ def user_config_value_to_proto(user_config_value: typing.UserConfigValue) -> Sca
|
|
|
548
550
|
)
|
|
549
551
|
|
|
550
552
|
|
|
551
|
-
def user_config_value_from_proto(scalar_msg: Scalar) ->
|
|
553
|
+
def user_config_value_from_proto(scalar_msg: Scalar) -> UserConfigValue:
|
|
552
554
|
"""Deserialize `UserConfigValue` from ProtoBuf."""
|
|
553
555
|
scalar_field = scalar_msg.WhichOneof("scalar")
|
|
554
556
|
scalar = getattr(scalar_msg, cast(str, scalar_field))
|
|
555
|
-
return cast(
|
|
557
|
+
return cast(UserConfigValue, scalar)
|
|
556
558
|
|
|
557
559
|
|
|
558
560
|
# === Message messages ===
|
flwr/common/telemetry.py
CHANGED
|
@@ -28,7 +28,7 @@ from pathlib import Path
|
|
|
28
28
|
from typing import Any, cast
|
|
29
29
|
|
|
30
30
|
from flwr.common.constant import FLWR_DIR
|
|
31
|
-
from flwr.
|
|
31
|
+
from flwr.supercore.version import package_name, package_version
|
|
32
32
|
|
|
33
33
|
FLWR_TELEMETRY_ENABLED = os.getenv("FLWR_TELEMETRY_ENABLED", "1")
|
|
34
34
|
FLWR_TELEMETRY_LOGGING = os.getenv("FLWR_TELEMETRY_LOGGING", "0")
|
flwr/common/typing.py
CHANGED
|
@@ -23,6 +23,8 @@ from typing import Any
|
|
|
23
23
|
import numpy as np
|
|
24
24
|
import numpy.typing as npt
|
|
25
25
|
|
|
26
|
+
from flwr.app.user_config import UserConfig
|
|
27
|
+
from flwr.proto.federation_pb2 import Account # pylint: disable=E0611
|
|
26
28
|
from flwr.proto.node_pb2 import NodeInfo # pylint: disable=E0611
|
|
27
29
|
|
|
28
30
|
NDArray = npt.NDArray[Any]
|
|
@@ -64,8 +66,6 @@ Config = dict[str, Scalar]
|
|
|
64
66
|
Properties = dict[str, Scalar]
|
|
65
67
|
|
|
66
68
|
# Value type for user configs
|
|
67
|
-
UserConfigValue = bool | float | int | str
|
|
68
|
-
UserConfig = dict[str, UserConfigValue]
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
class Code(Enum):
|
|
@@ -343,6 +343,7 @@ class Federation:
|
|
|
343
343
|
"""Federation details."""
|
|
344
344
|
|
|
345
345
|
name: str
|
|
346
|
-
|
|
346
|
+
description: str
|
|
347
|
+
accounts: list[Account]
|
|
347
348
|
nodes: list[NodeInfo]
|
|
348
349
|
runs: list[Run]
|
flwr/compat/client/app.py
CHANGED
|
@@ -25,6 +25,7 @@ from cryptography.hazmat.primitives.asymmetric import ec
|
|
|
25
25
|
from grpc import RpcError
|
|
26
26
|
|
|
27
27
|
from flwr.app.error import Error
|
|
28
|
+
from flwr.app.user_config import UserConfig
|
|
28
29
|
from flwr.cli.config_utils import get_fab_metadata
|
|
29
30
|
from flwr.cli.install import install_from_fab
|
|
30
31
|
from flwr.client.client import Client
|
|
@@ -34,18 +35,14 @@ from flwr.client.run_info_store import DeprecatedRunInfoStore
|
|
|
34
35
|
from flwr.client.typing import ClientFnExt
|
|
35
36
|
from flwr.clientapp.client_app import ClientApp, LoadClientAppError
|
|
36
37
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH, Context, EventType, Message, event
|
|
37
|
-
from flwr.common.
|
|
38
|
-
from flwr.common.constant import (
|
|
39
|
-
MAX_RETRY_DELAY,
|
|
40
|
-
TRANSPORT_TYPE_GRPC_BIDI,
|
|
41
|
-
TRANSPORT_TYPES,
|
|
42
|
-
ErrorCode,
|
|
43
|
-
)
|
|
38
|
+
from flwr.common.constant import MAX_RETRY_DELAY, ErrorCode
|
|
44
39
|
from flwr.common.exit import ExitCode, flwr_exit
|
|
45
40
|
from flwr.common.logger import log, warn_deprecated_feature
|
|
46
41
|
from flwr.common.retry_invoker import RetryInvoker, RetryState, exponential
|
|
47
|
-
from flwr.common.typing import Fab, Run, RunNotRunningException
|
|
42
|
+
from flwr.common.typing import Fab, Run, RunNotRunningException
|
|
48
43
|
from flwr.compat.client.grpc_client.connection import grpc_connection
|
|
44
|
+
from flwr.compat.common.constant import TRANSPORT_TYPE_GRPC_BIDI, TRANSPORT_TYPES_COMPAT
|
|
45
|
+
from flwr.supercore.address import parse_address
|
|
49
46
|
from flwr.supercore.object_store import ObjectStoreFactory
|
|
50
47
|
from flwr.supernode.nodestate import NodeStateFactory
|
|
51
48
|
|
|
@@ -675,7 +672,7 @@ def _init_connection(transport: str | None, server_address: str) -> tuple[
|
|
|
675
672
|
connection, error_type = grpc_connection, RpcError
|
|
676
673
|
else:
|
|
677
674
|
raise ValueError(
|
|
678
|
-
f"Unknown transport type: {transport} (possible: {
|
|
675
|
+
f"Unknown transport type: {transport} (possible: {TRANSPORT_TYPES_COMPAT})"
|
|
679
676
|
)
|
|
680
677
|
|
|
681
678
|
return connection, address, error_type
|
|
@@ -25,6 +25,7 @@ from typing import cast
|
|
|
25
25
|
|
|
26
26
|
from cryptography.hazmat.primitives.asymmetric import ec
|
|
27
27
|
|
|
28
|
+
from flwr.app.message_type import MessageType
|
|
28
29
|
from flwr.common import (
|
|
29
30
|
DEFAULT_TTL,
|
|
30
31
|
GRPC_MAX_MESSAGE_LENGTH,
|
|
@@ -36,7 +37,7 @@ from flwr.common import (
|
|
|
36
37
|
)
|
|
37
38
|
from flwr.common import recorddict_compat as compat
|
|
38
39
|
from flwr.common import serde
|
|
39
|
-
from flwr.common.constant import
|
|
40
|
+
from flwr.common.constant import MessageTypeLegacy
|
|
40
41
|
from flwr.common.grpc import create_channel, on_channel_state_change
|
|
41
42
|
from flwr.common.logger import log
|
|
42
43
|
from flwr.common.message import make_message
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Copyright 2025 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 constants."""
|
|
16
|
+
|
|
17
|
+
from flwr.common.constant import (
|
|
18
|
+
TRANSPORT_TYPE_GRPC_RERE,
|
|
19
|
+
TRANSPORT_TYPE_REST,
|
|
20
|
+
TRANSPORT_TYPE_VCE,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
TRANSPORT_TYPE_GRPC_BIDI = "grpc-bidi"
|
|
24
|
+
TRANSPORT_TYPES_COMPAT = [
|
|
25
|
+
TRANSPORT_TYPE_GRPC_BIDI,
|
|
26
|
+
TRANSPORT_TYPE_GRPC_RERE,
|
|
27
|
+
TRANSPORT_TYPE_REST,
|
|
28
|
+
TRANSPORT_TYPE_VCE,
|
|
29
|
+
]
|
flwr/compat/server/app.py
CHANGED
|
@@ -19,7 +19,6 @@ import sys
|
|
|
19
19
|
from logging import INFO
|
|
20
20
|
|
|
21
21
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH, EventType, event
|
|
22
|
-
from flwr.common.address import parse_address
|
|
23
22
|
from flwr.common.constant import FLEET_API_GRPC_BIDI_DEFAULT_ADDRESS
|
|
24
23
|
from flwr.common.exit import register_signal_handlers
|
|
25
24
|
from flwr.common.logger import log, warn_deprecated_feature
|
|
@@ -29,6 +28,7 @@ from flwr.server.server import Server, init_defaults, run_fl
|
|
|
29
28
|
from flwr.server.server_config import ServerConfig
|
|
30
29
|
from flwr.server.strategy import Strategy
|
|
31
30
|
from flwr.server.superlink.fleet.grpc_bidi.grpc_server import start_grpc_server
|
|
31
|
+
from flwr.supercore.address import parse_address
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def start_server( # pylint: disable=too-many-arguments,too-many-locals
|
flwr/proto/clientappio_pb2.py
CHANGED
|
@@ -28,7 +28,7 @@ from flwr.proto import message_pb2 as flwr_dot_proto_dot_message__pb2
|
|
|
28
28
|
from flwr.proto import appio_pb2 as flwr_dot_proto_dot_appio__pb2
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x66lwr/proto/clientappio.proto\x12\nflwr.proto\x1a\x1a\x66lwr/proto/heartbeat.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x18\x66lwr/proto/message.proto\x1a\x16\x66lwr/proto/appio.proto2\
|
|
31
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x66lwr/proto/clientappio.proto\x12\nflwr.proto\x1a\x1a\x66lwr/proto/heartbeat.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x18\x66lwr/proto/message.proto\x1a\x16\x66lwr/proto/appio.proto2\xdf\x07\n\x0b\x43lientAppIo\x12_\n\x10ListAppsToLaunch\x12#.flwr.proto.ListAppsToLaunchRequest\x1a$.flwr.proto.ListAppsToLaunchResponse\"\x00\x12S\n\x0cRequestToken\x12\x1f.flwr.proto.RequestTokenRequest\x1a .flwr.proto.RequestTokenResponse\"\x00\x12\x41\n\x06GetRun\x12\x19.flwr.proto.GetRunRequest\x1a\x1a.flwr.proto.GetRunResponse\"\x00\x12_\n\x10SendAppHeartbeat\x12#.flwr.proto.SendAppHeartbeatRequest\x1a$.flwr.proto.SendAppHeartbeatResponse\"\x00\x12V\n\rPullAppInputs\x12 .flwr.proto.PullAppInputsRequest\x1a!.flwr.proto.PullAppInputsResponse\"\x00\x12Y\n\x0ePushAppOutputs\x12!.flwr.proto.PushAppOutputsRequest\x1a\".flwr.proto.PushAppOutputsResponse\"\x00\x12M\n\nPushObject\x12\x1d.flwr.proto.PushObjectRequest\x1a\x1e.flwr.proto.PushObjectResponse\"\x00\x12M\n\nPullObject\x12\x1d.flwr.proto.PullObjectRequest\x1a\x1e.flwr.proto.PullObjectResponse\"\x00\x12q\n\x16\x43onfirmMessageReceived\x12).flwr.proto.ConfirmMessageReceivedRequest\x1a*.flwr.proto.ConfirmMessageReceivedResponse\"\x00\x12X\n\x0bPushMessage\x12\".flwr.proto.PushAppMessagesRequest\x1a#.flwr.proto.PushAppMessagesResponse\"\x00\x12X\n\x0bPullMessage\x12\".flwr.proto.PullAppMessagesRequest\x1a#.flwr.proto.PullAppMessagesResponse\"\x00\x62\x06proto3')
|
|
32
32
|
|
|
33
33
|
_globals = globals()
|
|
34
34
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
@@ -36,5 +36,5 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'flwr.proto.clientappio_pb2'
|
|
|
36
36
|
if not _descriptor._USE_C_DESCRIPTORS:
|
|
37
37
|
DESCRIPTOR._loaded_options = None
|
|
38
38
|
_globals['_CLIENTAPPIO']._serialized_start=145
|
|
39
|
-
_globals['_CLIENTAPPIO']._serialized_end=
|
|
39
|
+
_globals['_CLIENTAPPIO']._serialized_end=1136
|
|
40
40
|
# @@protoc_insertion_point(module_scope)
|