flwr-nightly 1.8.0.dev20240305__py3-none-any.whl → 1.8.0.dev20240306__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/cli/app.py CHANGED
@@ -18,6 +18,7 @@ import typer
18
18
 
19
19
  from .example import example
20
20
  from .new import new
21
+ from .run import run
21
22
 
22
23
  app = typer.Typer(
23
24
  help=typer.style(
@@ -30,6 +31,7 @@ app = typer.Typer(
30
31
 
31
32
  app.command()(new)
32
33
  app.command()(example)
34
+ app.command()(run)
33
35
 
34
36
  if __name__ == "__main__":
35
37
  app()
@@ -0,0 +1,151 @@
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
+ """Utility to validate the `flower.toml` file."""
16
+
17
+ import importlib
18
+ import os
19
+ from typing import Any, Dict, List, Optional, Tuple
20
+
21
+ import tomli
22
+
23
+
24
+ def load_flower_toml(path: Optional[str] = None) -> Optional[Dict[str, Any]]:
25
+ """Load flower.toml and return as dict."""
26
+ if path is None:
27
+ cur_dir = os.getcwd()
28
+ toml_path = os.path.join(cur_dir, "flower.toml")
29
+ else:
30
+ toml_path = path
31
+
32
+ if not os.path.isfile(toml_path):
33
+ return None
34
+
35
+ with open(toml_path, encoding="utf-8") as toml_file:
36
+ data = tomli.loads(toml_file.read())
37
+ return data
38
+
39
+
40
+ def validate_flower_toml_fields(
41
+ config: Dict[str, Any]
42
+ ) -> Tuple[bool, List[str], List[str]]:
43
+ """Validate flower.toml fields."""
44
+ errors = []
45
+ warnings = []
46
+
47
+ if "project" not in config:
48
+ errors.append("Missing [project] section")
49
+ else:
50
+ if "name" not in config["project"]:
51
+ errors.append('Property "name" missing in [project]')
52
+ if "version" not in config["project"]:
53
+ errors.append('Property "version" missing in [project]')
54
+ if "description" not in config["project"]:
55
+ warnings.append('Recommended property "description" missing in [project]')
56
+ if "license" not in config["project"]:
57
+ warnings.append('Recommended property "license" missing in [project]')
58
+ if "authors" not in config["project"]:
59
+ warnings.append('Recommended property "authors" missing in [project]')
60
+
61
+ if "flower" not in config:
62
+ errors.append("Missing [flower] section")
63
+ elif "components" not in config["flower"]:
64
+ errors.append("Missing [flower.components] section")
65
+ else:
66
+ if "serverapp" not in config["flower"]["components"]:
67
+ errors.append('Property "serverapp" missing in [flower.components]')
68
+ if "clientapp" not in config["flower"]["components"]:
69
+ errors.append('Property "clientapp" missing in [flower.components]')
70
+
71
+ return len(errors) == 0, errors, warnings
72
+
73
+
74
+ def validate_object_reference(ref: str) -> Tuple[bool, Optional[str]]:
75
+ """Validate object reference.
76
+
77
+ Returns
78
+ -------
79
+ Tuple[bool, Optional[str]]
80
+ A boolean indicating whether an object reference is valid and
81
+ the reason why it might not be.
82
+ """
83
+ module_str, _, attributes_str = ref.partition(":")
84
+ if not module_str:
85
+ return (
86
+ False,
87
+ f"Missing module in {ref}",
88
+ )
89
+ if not attributes_str:
90
+ return (
91
+ False,
92
+ f"Missing attribute in {ref}",
93
+ )
94
+
95
+ # Load module
96
+ try:
97
+ module = importlib.import_module(module_str)
98
+ except ModuleNotFoundError:
99
+ return False, f"Unable to load module {module_str}"
100
+
101
+ # Recursively load attribute
102
+ attribute = module
103
+ try:
104
+ for attribute_str in attributes_str.split("."):
105
+ attribute = getattr(attribute, attribute_str)
106
+ except AttributeError:
107
+ return (
108
+ False,
109
+ f"Unable to load attribute {attributes_str} from module {module_str}",
110
+ )
111
+
112
+ return (True, None)
113
+
114
+
115
+ def validate_flower_toml(config: Dict[str, Any]) -> Tuple[bool, List[str], List[str]]:
116
+ """Validate flower.toml."""
117
+ is_valid, errors, warnings = validate_flower_toml_fields(config)
118
+
119
+ if not is_valid:
120
+ return False, errors, warnings
121
+
122
+ # Validate serverapp
123
+ is_valid, reason = validate_object_reference(
124
+ config["flower"]["components"]["serverapp"]
125
+ )
126
+ if not is_valid and isinstance(reason, str):
127
+ return False, [reason], []
128
+
129
+ # Validate clientapp
130
+ is_valid, reason = validate_object_reference(
131
+ config["flower"]["components"]["clientapp"]
132
+ )
133
+
134
+ if not is_valid and isinstance(reason, str):
135
+ return False, [reason], []
136
+
137
+ return True, [], []
138
+
139
+
140
+ def apply_defaults(
141
+ config: Dict[str, Any],
142
+ defaults: Dict[str, Any],
143
+ ) -> Dict[str, Any]:
144
+ """Apply defaults to config."""
145
+ for key in defaults:
146
+ if key in config:
147
+ if isinstance(config[key], dict) and isinstance(defaults[key], dict):
148
+ apply_defaults(config[key], defaults[key])
149
+ else:
150
+ config[key] = defaults[key]
151
+ return config
@@ -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 `run` command."""
16
+
17
+ from .run import run as run
18
+
19
+ __all__ = [
20
+ "run",
21
+ ]
flwr/cli/run/run.py ADDED
@@ -0,0 +1,102 @@
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 `run` command."""
16
+
17
+ import sys
18
+
19
+ import typer
20
+
21
+ from flwr.cli.flower_toml import apply_defaults, load_flower_toml, validate_flower_toml
22
+ from flwr.simulation.run_simulation import _run_simulation
23
+
24
+
25
+ def run() -> None:
26
+ """Run Flower project."""
27
+ print(
28
+ typer.style("Loading project configuration... ", fg=typer.colors.BLUE),
29
+ end="",
30
+ )
31
+ config = load_flower_toml()
32
+ if not config:
33
+ print(
34
+ typer.style(
35
+ "Project configuration could not be loaded. "
36
+ "flower.toml does not exist.",
37
+ fg=typer.colors.RED,
38
+ bold=True,
39
+ )
40
+ )
41
+ sys.exit()
42
+ print(typer.style("Success", fg=typer.colors.GREEN))
43
+
44
+ print(
45
+ typer.style("Validating project configuration... ", fg=typer.colors.BLUE),
46
+ end="",
47
+ )
48
+ is_valid, errors, warnings = validate_flower_toml(config)
49
+ if warnings:
50
+ print(
51
+ typer.style(
52
+ "Project configuration is missing the following "
53
+ "recommended properties:\n"
54
+ + "\n".join([f"- {line}" for line in warnings]),
55
+ fg=typer.colors.RED,
56
+ bold=True,
57
+ )
58
+ )
59
+
60
+ if not is_valid:
61
+ print(
62
+ typer.style(
63
+ "Project configuration could not be loaded.\nflower.toml is invalid:\n"
64
+ + "\n".join([f"- {line}" for line in errors]),
65
+ fg=typer.colors.RED,
66
+ bold=True,
67
+ )
68
+ )
69
+ sys.exit()
70
+ print(typer.style("Success", fg=typer.colors.GREEN))
71
+
72
+ # Apply defaults
73
+ defaults = {
74
+ "flower": {
75
+ "engine": {"name": "simulation", "simulation": {"super-node": {"num": 100}}}
76
+ }
77
+ }
78
+ config = apply_defaults(config, defaults)
79
+
80
+ server_app_ref = config["flower"]["components"]["serverapp"]
81
+ client_app_ref = config["flower"]["components"]["clientapp"]
82
+ engine = config["flower"]["engine"]["name"]
83
+
84
+ if engine == "simulation":
85
+ num_supernodes = config["flower"]["engine"]["simulation"]["super-node"]["num"]
86
+
87
+ print(
88
+ typer.style("Starting run... ", fg=typer.colors.BLUE),
89
+ )
90
+ _run_simulation(
91
+ server_app_attr=server_app_ref,
92
+ client_app_attr=client_app_ref,
93
+ num_supernodes=num_supernodes,
94
+ )
95
+ else:
96
+ print(
97
+ typer.style(
98
+ f"Engine '{engine}' is not yet supported in `flwr run`",
99
+ fg=typer.colors.RED,
100
+ bold=True,
101
+ )
102
+ )
@@ -31,12 +31,7 @@ from flwr.common import (
31
31
  )
32
32
  from flwr.common import recordset_compat as compat
33
33
  from flwr.common import serde
34
- from flwr.common.constant import (
35
- MESSAGE_TYPE_EVALUATE,
36
- MESSAGE_TYPE_FIT,
37
- MESSAGE_TYPE_GET_PARAMETERS,
38
- MESSAGE_TYPE_GET_PROPERTIES,
39
- )
34
+ from flwr.common.constant import MessageType, MessageTypeLegacy
40
35
  from flwr.common.grpc import create_channel
41
36
  from flwr.common.logger import log
42
37
  from flwr.common.retry_invoker import RetryInvoker
@@ -148,22 +143,22 @@ def grpc_connection( # pylint: disable=R0915
148
143
  recordset = compat.getpropertiesins_to_recordset(
149
144
  serde.get_properties_ins_from_proto(proto.get_properties_ins)
150
145
  )
151
- message_type = MESSAGE_TYPE_GET_PROPERTIES
146
+ message_type = MessageTypeLegacy.GET_PROPERTIES
152
147
  elif field == "get_parameters_ins":
153
148
  recordset = compat.getparametersins_to_recordset(
154
149
  serde.get_parameters_ins_from_proto(proto.get_parameters_ins)
155
150
  )
156
- message_type = MESSAGE_TYPE_GET_PARAMETERS
151
+ message_type = MessageTypeLegacy.GET_PARAMETERS
157
152
  elif field == "fit_ins":
158
153
  recordset = compat.fitins_to_recordset(
159
154
  serde.fit_ins_from_proto(proto.fit_ins), False
160
155
  )
161
- message_type = MESSAGE_TYPE_FIT
156
+ message_type = MessageType.TRAIN
162
157
  elif field == "evaluate_ins":
163
158
  recordset = compat.evaluateins_to_recordset(
164
159
  serde.evaluate_ins_from_proto(proto.evaluate_ins), False
165
160
  )
166
- message_type = MESSAGE_TYPE_EVALUATE
161
+ message_type = MessageType.EVALUATE
167
162
  elif field == "reconnect_ins":
168
163
  recordset = RecordSet()
169
164
  recordset.configs_records["config"] = ConfigsRecord(
@@ -197,20 +192,20 @@ def grpc_connection( # pylint: disable=R0915
197
192
  message_type = message.metadata.message_type
198
193
 
199
194
  # RecordSet --> *Res --> *Res proto -> ClientMessage proto
200
- if message_type == MESSAGE_TYPE_GET_PROPERTIES:
195
+ if message_type == MessageTypeLegacy.GET_PROPERTIES:
201
196
  getpropres = compat.recordset_to_getpropertiesres(recordset)
202
197
  msg_proto = ClientMessage(
203
198
  get_properties_res=serde.get_properties_res_to_proto(getpropres)
204
199
  )
205
- elif message_type == MESSAGE_TYPE_GET_PARAMETERS:
200
+ elif message_type == MessageTypeLegacy.GET_PARAMETERS:
206
201
  getparamres = compat.recordset_to_getparametersres(recordset, False)
207
202
  msg_proto = ClientMessage(
208
203
  get_parameters_res=serde.get_parameters_res_to_proto(getparamres)
209
204
  )
210
- elif message_type == MESSAGE_TYPE_FIT:
205
+ elif message_type == MessageType.TRAIN:
211
206
  fitres = compat.recordset_to_fitres(recordset, False)
212
207
  msg_proto = ClientMessage(fit_res=serde.fit_res_to_proto(fitres))
213
- elif message_type == MESSAGE_TYPE_EVALUATE:
208
+ elif message_type == MessageType.EVALUATE:
214
209
  evalres = compat.recordset_to_evaluateres(recordset)
215
210
  msg_proto = ClientMessage(evaluate_res=serde.evaluate_res_to_proto(evalres))
216
211
  elif message_type == "reconnect":
@@ -27,12 +27,7 @@ from flwr.client.client import (
27
27
  from flwr.client.numpy_client import NumPyClient
28
28
  from flwr.client.typing import ClientFn
29
29
  from flwr.common import ConfigsRecord, Context, Message, Metadata, RecordSet, log
30
- from flwr.common.constant import (
31
- MESSAGE_TYPE_EVALUATE,
32
- MESSAGE_TYPE_FIT,
33
- MESSAGE_TYPE_GET_PARAMETERS,
34
- MESSAGE_TYPE_GET_PROPERTIES,
35
- )
30
+ from flwr.common.constant import MessageType, MessageTypeLegacy
36
31
  from flwr.common.recordset_compat import (
37
32
  evaluateres_to_recordset,
38
33
  fitres_to_recordset,
@@ -115,14 +110,14 @@ def handle_legacy_message_from_msgtype(
115
110
  message_type = message.metadata.message_type
116
111
 
117
112
  # Handle GetPropertiesIns
118
- if message_type == MESSAGE_TYPE_GET_PROPERTIES:
113
+ if message_type == MessageTypeLegacy.GET_PROPERTIES:
119
114
  get_properties_res = maybe_call_get_properties(
120
115
  client=client,
121
116
  get_properties_ins=recordset_to_getpropertiesins(message.content),
122
117
  )
123
118
  out_recordset = getpropertiesres_to_recordset(get_properties_res)
124
119
  # Handle GetParametersIns
125
- elif message_type == MESSAGE_TYPE_GET_PARAMETERS:
120
+ elif message_type == MessageTypeLegacy.GET_PARAMETERS:
126
121
  get_parameters_res = maybe_call_get_parameters(
127
122
  client=client,
128
123
  get_parameters_ins=recordset_to_getparametersins(message.content),
@@ -131,14 +126,14 @@ def handle_legacy_message_from_msgtype(
131
126
  get_parameters_res, keep_input=False
132
127
  )
133
128
  # Handle FitIns
134
- elif message_type == MESSAGE_TYPE_FIT:
129
+ elif message_type == MessageType.TRAIN:
135
130
  fit_res = maybe_call_fit(
136
131
  client=client,
137
132
  fit_ins=recordset_to_fitins(message.content, keep_input=True),
138
133
  )
139
134
  out_recordset = fitres_to_recordset(fit_res, keep_input=False)
140
135
  # Handle EvaluateIns
141
- elif message_type == MESSAGE_TYPE_EVALUATE:
136
+ elif message_type == MessageType.EVALUATE:
142
137
  evaluate_res = maybe_call_evaluate(
143
138
  client=client,
144
139
  evaluate_ins=recordset_to_evaluateins(message.content, keep_input=True),
@@ -18,7 +18,7 @@
18
18
  from flwr.client.typing import ClientAppCallable
19
19
  from flwr.common import ndarrays_to_parameters, parameters_to_ndarrays
20
20
  from flwr.common import recordset_compat as compat
21
- from flwr.common.constant import MESSAGE_TYPE_FIT
21
+ from flwr.common.constant import MessageType
22
22
  from flwr.common.context import Context
23
23
  from flwr.common.differential_privacy import (
24
24
  compute_adaptive_clip_model_update,
@@ -40,7 +40,7 @@ def fixedclipping_mod(
40
40
 
41
41
  This mod clips the client model updates before sending them to the server.
42
42
 
43
- It operates on messages with type MESSAGE_TYPE_FIT.
43
+ It operates on messages with type MessageType.TRAIN.
44
44
 
45
45
  Notes
46
46
  -----
@@ -48,7 +48,7 @@ def fixedclipping_mod(
48
48
 
49
49
  Typically, fixedclipping_mod should be the last to operate on params.
50
50
  """
51
- if msg.metadata.message_type != MESSAGE_TYPE_FIT:
51
+ if msg.metadata.message_type != MessageType.TRAIN:
52
52
  return call_next(msg, ctxt)
53
53
  fit_ins = compat.recordset_to_fitins(msg.content, keep_input=True)
54
54
  if KEY_CLIPPING_NORM not in fit_ins.config:
@@ -93,7 +93,7 @@ def adaptiveclipping_mod(
93
93
 
94
94
  It also sends KEY_NORM_BIT to the server for computing the new clipping value.
95
95
 
96
- It operates on messages with type MESSAGE_TYPE_FIT.
96
+ It operates on messages with type MessageType.TRAIN.
97
97
 
98
98
  Notes
99
99
  -----
@@ -101,7 +101,7 @@ def adaptiveclipping_mod(
101
101
 
102
102
  Typically, adaptiveclipping_mod should be the last to operate on params.
103
103
  """
104
- if msg.metadata.message_type != MESSAGE_TYPE_FIT:
104
+ if msg.metadata.message_type != MessageType.TRAIN:
105
105
  return call_next(msg, ctxt)
106
106
 
107
107
  fit_ins = compat.recordset_to_fitins(msg.content, keep_input=True)
@@ -30,7 +30,7 @@ from flwr.common import (
30
30
  parameters_to_ndarrays,
31
31
  )
32
32
  from flwr.common import recordset_compat as compat
33
- from flwr.common.constant import MESSAGE_TYPE_FIT
33
+ from flwr.common.constant import MessageType
34
34
  from flwr.common.logger import log
35
35
  from flwr.common.secure_aggregation.crypto.shamir import create_shares
36
36
  from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
@@ -150,7 +150,7 @@ def secaggplus_mod(
150
150
  ) -> Message:
151
151
  """Handle incoming message and return results, following the SecAgg+ protocol."""
152
152
  # Ignore non-fit messages
153
- if msg.metadata.message_type != MESSAGE_TYPE_FIT:
153
+ if msg.metadata.message_type != MessageType.TRAIN:
154
154
  return call_next(msg, ctxt)
155
155
 
156
156
  # Retrieve local state
flwr/common/__init__.py CHANGED
@@ -16,6 +16,7 @@
16
16
 
17
17
 
18
18
  from .constant import MessageType as MessageType
19
+ from .constant import MessageTypeLegacy as MessageTypeLegacy
19
20
  from .context import Context as Context
20
21
  from .date import now as now
21
22
  from .grpc import GRPC_MAX_MESSAGE_LENGTH
@@ -85,6 +86,7 @@ __all__ = [
85
86
  "log",
86
87
  "Message",
87
88
  "MessageType",
89
+ "MessageTypeLegacy",
88
90
  "Metadata",
89
91
  "Metrics",
90
92
  "MetricsAggregationFn",
flwr/common/constant.py CHANGED
@@ -36,11 +36,6 @@ TRANSPORT_TYPES = [
36
36
  TRANSPORT_TYPE_VCE,
37
37
  ]
38
38
 
39
- MESSAGE_TYPE_GET_PROPERTIES = "get_properties"
40
- MESSAGE_TYPE_GET_PARAMETERS = "get_parameters"
41
- MESSAGE_TYPE_FIT = "fit"
42
- MESSAGE_TYPE_EVALUATE = "evaluate"
43
-
44
39
 
45
40
  class MessageType:
46
41
  """Message type."""
@@ -53,6 +48,17 @@ class MessageType:
53
48
  raise TypeError(f"{cls.__name__} cannot be instantiated.")
54
49
 
55
50
 
51
+ class MessageTypeLegacy:
52
+ """Legacy message type."""
53
+
54
+ GET_PROPERTIES = "get_properties"
55
+ GET_PARAMETERS = "get_parameters"
56
+
57
+ def __new__(cls) -> MessageTypeLegacy:
58
+ """Prevent instantiation."""
59
+ raise TypeError(f"{cls.__name__} cannot be instantiated.")
60
+
61
+
56
62
  class SType:
57
63
  """Serialisation type."""
58
64
 
@@ -19,15 +19,9 @@ import time
19
19
  from typing import List, Optional
20
20
 
21
21
  from flwr import common
22
- from flwr.common import RecordSet
22
+ from flwr.common import MessageType, MessageTypeLegacy, RecordSet
23
23
  from flwr.common import recordset_compat as compat
24
24
  from flwr.common import serde
25
- from flwr.common.constant import (
26
- MESSAGE_TYPE_EVALUATE,
27
- MESSAGE_TYPE_FIT,
28
- MESSAGE_TYPE_GET_PARAMETERS,
29
- MESSAGE_TYPE_GET_PROPERTIES,
30
- )
31
25
  from flwr.proto import driver_pb2, node_pb2, task_pb2 # pylint: disable=E0611
32
26
  from flwr.server.client_proxy import ClientProxy
33
27
 
@@ -57,7 +51,7 @@ class DriverClientProxy(ClientProxy):
57
51
  out_recordset = compat.getpropertiesins_to_recordset(ins)
58
52
  # Fetch response
59
53
  in_recordset = self._send_receive_recordset(
60
- out_recordset, MESSAGE_TYPE_GET_PROPERTIES, timeout, group_id
54
+ out_recordset, MessageTypeLegacy.GET_PROPERTIES, timeout, group_id
61
55
  )
62
56
  # RecordSet to Res
63
57
  return compat.recordset_to_getpropertiesres(in_recordset)
@@ -73,7 +67,7 @@ class DriverClientProxy(ClientProxy):
73
67
  out_recordset = compat.getparametersins_to_recordset(ins)
74
68
  # Fetch response
75
69
  in_recordset = self._send_receive_recordset(
76
- out_recordset, MESSAGE_TYPE_GET_PARAMETERS, timeout, group_id
70
+ out_recordset, MessageTypeLegacy.GET_PARAMETERS, timeout, group_id
77
71
  )
78
72
  # RecordSet to Res
79
73
  return compat.recordset_to_getparametersres(in_recordset, False)
@@ -86,7 +80,7 @@ class DriverClientProxy(ClientProxy):
86
80
  out_recordset = compat.fitins_to_recordset(ins, keep_input=True)
87
81
  # Fetch response
88
82
  in_recordset = self._send_receive_recordset(
89
- out_recordset, MESSAGE_TYPE_FIT, timeout, group_id
83
+ out_recordset, MessageType.TRAIN, timeout, group_id
90
84
  )
91
85
  # RecordSet to Res
92
86
  return compat.recordset_to_fitres(in_recordset, keep_input=False)
@@ -99,7 +93,7 @@ class DriverClientProxy(ClientProxy):
99
93
  out_recordset = compat.evaluateins_to_recordset(ins, keep_input=True)
100
94
  # Fetch response
101
95
  in_recordset = self._send_receive_recordset(
102
- out_recordset, MESSAGE_TYPE_EVALUATE, timeout, group_id
96
+ out_recordset, MessageType.EVALUATE, timeout, group_id
103
97
  )
104
98
  # RecordSet to Res
105
99
  return compat.recordset_to_evaluateres(in_recordset)
@@ -21,11 +21,7 @@ from typing import Optional, cast
21
21
 
22
22
  import flwr.common.recordset_compat as compat
23
23
  from flwr.common import ConfigsRecord, Context, GetParametersIns, log
24
- from flwr.common.constant import (
25
- MESSAGE_TYPE_EVALUATE,
26
- MESSAGE_TYPE_FIT,
27
- MESSAGE_TYPE_GET_PARAMETERS,
28
- )
24
+ from flwr.common.constant import MessageType, MessageTypeLegacy
29
25
 
30
26
  from ..compat.app_utils import start_update_client_manager_thread
31
27
  from ..compat.legacy_context import LegacyContext
@@ -134,7 +130,7 @@ def default_init_params_workflow(driver: Driver, context: Context) -> None:
134
130
  [
135
131
  driver.create_message(
136
132
  content=content,
137
- message_type=MESSAGE_TYPE_GET_PARAMETERS,
133
+ message_type=MessageTypeLegacy.GET_PARAMETERS,
138
134
  dst_node_id=random_client.node_id,
139
135
  group_id="",
140
136
  ttl="",
@@ -232,7 +228,7 @@ def default_fit_workflow(driver: Driver, context: Context) -> None:
232
228
  out_messages = [
233
229
  driver.create_message(
234
230
  content=compat.fitins_to_recordset(fitins, True),
235
- message_type=MESSAGE_TYPE_FIT,
231
+ message_type=MessageType.TRAIN,
236
232
  dst_node_id=proxy.node_id,
237
233
  group_id="",
238
234
  ttl="",
@@ -313,7 +309,7 @@ def default_evaluate_workflow(driver: Driver, context: Context) -> None:
313
309
  out_messages = [
314
310
  driver.create_message(
315
311
  content=compat.evaluateins_to_recordset(evalins, True),
316
- message_type=MESSAGE_TYPE_EVALUATE,
312
+ message_type=MessageType.EVALUATE,
317
313
  dst_node_id=proxy.node_id,
318
314
  group_id="",
319
315
  ttl="",
@@ -24,12 +24,7 @@ from flwr.client import ClientFn
24
24
  from flwr.client.client_app import ClientApp
25
25
  from flwr.client.node_state import NodeState
26
26
  from flwr.common import Message, Metadata, RecordSet
27
- from flwr.common.constant import (
28
- MESSAGE_TYPE_EVALUATE,
29
- MESSAGE_TYPE_FIT,
30
- MESSAGE_TYPE_GET_PARAMETERS,
31
- MESSAGE_TYPE_GET_PROPERTIES,
32
- )
27
+ from flwr.common.constant import MessageType, MessageTypeLegacy
33
28
  from flwr.common.logger import log
34
29
  from flwr.common.recordset_compat import (
35
30
  evaluateins_to_recordset,
@@ -126,7 +121,7 @@ class RayActorClientProxy(ClientProxy):
126
121
  recordset = getpropertiesins_to_recordset(ins)
127
122
  message = self._wrap_recordset_in_message(
128
123
  recordset,
129
- message_type=MESSAGE_TYPE_GET_PROPERTIES,
124
+ message_type=MessageTypeLegacy.GET_PROPERTIES,
130
125
  timeout=timeout,
131
126
  group_id=group_id,
132
127
  )
@@ -145,7 +140,7 @@ class RayActorClientProxy(ClientProxy):
145
140
  recordset = getparametersins_to_recordset(ins)
146
141
  message = self._wrap_recordset_in_message(
147
142
  recordset,
148
- message_type=MESSAGE_TYPE_GET_PARAMETERS,
143
+ message_type=MessageTypeLegacy.GET_PARAMETERS,
149
144
  timeout=timeout,
150
145
  group_id=group_id,
151
146
  )
@@ -163,7 +158,7 @@ class RayActorClientProxy(ClientProxy):
163
158
  ) # This must stay TRUE since ins are in-memory
164
159
  message = self._wrap_recordset_in_message(
165
160
  recordset,
166
- message_type=MESSAGE_TYPE_FIT,
161
+ message_type=MessageType.TRAIN,
167
162
  timeout=timeout,
168
163
  group_id=group_id,
169
164
  )
@@ -181,7 +176,7 @@ class RayActorClientProxy(ClientProxy):
181
176
  ) # This must stay TRUE since ins are in-memory
182
177
  message = self._wrap_recordset_in_message(
183
178
  recordset,
184
- message_type=MESSAGE_TYPE_EVALUATE,
179
+ message_type=MessageType.EVALUATE,
185
180
  timeout=timeout,
186
181
  group_id=group_id,
187
182
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.8.0.dev20240305
3
+ Version: 1.8.0.dev20240306
4
4
  Summary: Flower: A Friendly Federated Learning Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -42,6 +42,7 @@ Requires-Dist: pydantic (<2.0.0) ; extra == "simulation"
42
42
  Requires-Dist: ray (==2.6.3) ; extra == "simulation"
43
43
  Requires-Dist: requests (>=2.31.0,<3.0.0) ; extra == "rest"
44
44
  Requires-Dist: starlette (>=0.31.0,<0.32.0) ; extra == "rest"
45
+ Requires-Dist: tomli (>=2.0.1,<3.0.0)
45
46
  Requires-Dist: typer[all] (>=0.9.0,<0.10.0)
46
47
  Requires-Dist: uvicorn[standard] (>=0.23.0,<0.24.0) ; extra == "rest"
47
48
  Project-URL: Documentation, https://flower.ai
@@ -182,7 +183,6 @@ Quickstart examples:
182
183
  - [Quickstart (JAX)](https://github.com/adap/flower/tree/main/examples/quickstart-jax)
183
184
  - [Quickstart (MONAI)](https://github.com/adap/flower/tree/main/examples/quickstart-monai)
184
185
  - [Quickstart (scikit-learn)](https://github.com/adap/flower/tree/main/examples/sklearn-logreg-mnist)
185
- - [Quickstart (XGBoost)](https://github.com/adap/flower/tree/main/examples/xgboost-quickstart)
186
186
  - [Quickstart (Android [TFLite])](https://github.com/adap/flower/tree/main/examples/android)
187
187
  - [Quickstart (iOS [CoreML])](https://github.com/adap/flower/tree/main/examples/ios)
188
188
  - [Quickstart (MLX)](https://github.com/adap/flower/tree/main/examples/quickstart-mlx)
@@ -194,7 +194,8 @@ Other [examples](https://github.com/adap/flower/tree/main/examples):
194
194
  - [PyTorch: From Centralized to Federated](https://github.com/adap/flower/tree/main/examples/pytorch-from-centralized-to-federated)
195
195
  - [Vertical FL](https://github.com/adap/flower/tree/main/examples/vertical-fl)
196
196
  - [Federated Finetuning of OpenAI's Whisper](https://github.com/adap/flower/tree/main/examples/whisper-federated-finetuning)
197
- - [Comprehensive XGBoost](https://github.com/adap/flower/tree/main/examples/xgboost-comprehensive)
197
+ - [Federated Finetuning of Large Language Model](https://github.com/adap/flower/tree/main/examples/fedllm-finetune)
198
+ - [Federated Finetuning of a Vision Transformer](https://github.com/adap/flower/tree/main/examples/vit-finetune)
198
199
  - [Advanced Flower with TensorFlow/Keras](https://github.com/adap/flower/tree/main/examples/advanced-tensorflow)
199
200
  - [Advanced Flower with PyTorch](https://github.com/adap/flower/tree/main/examples/advanced-pytorch)
200
201
  - Single-Machine Simulation of Federated Learning Systems ([PyTorch](https://github.com/adap/flower/tree/main/examples/simulation-pytorch)) ([Tensorflow](https://github.com/adap/flower/tree/main/examples/simulation-tensorflow))
@@ -1,7 +1,8 @@
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=RRW6dRjmtlRSK4KhFf3pt2KvEYjM2svDcnZyuqOhJY0,1055
3
+ flwr/cli/app.py,sha256=38thPnMydBmNAxNE9mz4By-KdRUhJfoUgeDuAxMYF_U,1095
4
4
  flwr/cli/example.py,sha256=EGPYLMQf2MgcYRn5aPp_eYYUA39M8dm69PUM4zhlHuk,2184
5
+ flwr/cli/flower_toml.py,sha256=SKnE9F8IWvg4F7rAIX1k89kQQ0LhHh08S4zexRRgOrA,4914
5
6
  flwr/cli/new/__init__.py,sha256=cQzK1WH4JP2awef1t2UQ2xjl1agVEz9rwutV18SWV1k,789
6
7
  flwr/cli/new/new.py,sha256=xQU4ouEHP2AntWWAqkfQup0GUFczvGqD9_yHf-U6AsU,4165
7
8
  flwr/cli/new/templates/__init__.py,sha256=4luU8RL-CK8JJCstQ_ON809W9bNTkY1l9zSaPKBkgwY,725
@@ -19,6 +20,8 @@ flwr/cli/new/templates/app/flower.toml.tpl,sha256=fLYif3PC2hoWwNn8kTAyUSmDh7jbyh
19
20
  flwr/cli/new/templates/app/requirements.numpy.txt.tpl,sha256=xd6_D1E6x3orei85nG1jSUcR_PhbIqEEwDiSW3ILQYY,32
20
21
  flwr/cli/new/templates/app/requirements.pytorch.txt.tpl,sha256=9Z70jsiCPdsbuorhicrSdO6PVQn-3196vKZ5Ka2GkK0,87
21
22
  flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl,sha256=WTbIgK5G_iG0W-xtvQLCZMxL_Og26rFXial2TkYH5dw,211
23
+ flwr/cli/run/__init__.py,sha256=oCd6HmQDx-sqver1gecgx-uMA38BLTSiiKpl7RGNceg,789
24
+ flwr/cli/run/run.py,sha256=N9FLGPmtokZqe0xJQ5PcxTpWDtTHcUIklSjaRS-Mwvk,3290
22
25
  flwr/cli/utils.py,sha256=9cCEIt8QmJXz85JNmPk1IHPd7p8E3KDn6h5CfF0nDL4,1926
23
26
  flwr/client/__init__.py,sha256=futk_IdY_N1h8BTve4Iru51bxm7H1gv58ZPIXWi5XUA,1187
24
27
  flwr/client/app.py,sha256=Z_RfL0i-h0Cr3A4_DZrX258c6aWG4aJkO8fYtJYy_kU,23542
@@ -26,16 +29,16 @@ flwr/client/client.py,sha256=Vp9UkOkoHdNfn6iMYZsj_5m_GICiFfUlKEVaLad-YhM,8183
26
29
  flwr/client/client_app.py,sha256=jrDgJBswP2hD1YdGgQoI3GU_NkliYWVU8glBJLOVzQY,4205
27
30
  flwr/client/dpfedavg_numpy_client.py,sha256=9Tnig4iml2J88HBKNahegjXjbfvIQyBtaIQaqjbeqsA,7435
28
31
  flwr/client/grpc_client/__init__.py,sha256=LsnbqXiJhgQcB0XzAlUQgPx011Uf7Y7yabIC1HxivJ8,735
29
- flwr/client/grpc_client/connection.py,sha256=opd-6ISbqsO487Nijts3JswNKk7cY28QE8ZCaMbJV24,8759
32
+ flwr/client/grpc_client/connection.py,sha256=5h8WD_gJgEDTwg-PNZRR4pm6LTS7eNvYKdNbq-uoi_U,8691
30
33
  flwr/client/grpc_rere_client/__init__.py,sha256=avn6W_vHEM_yZEB1S7hCZgnTbXb6ZujqRP_vAzyXu-0,752
31
34
  flwr/client/grpc_rere_client/connection.py,sha256=3kpnUbS06rNQ969EybGx7zZfQPc2JmCuViyrIt610V0,7421
32
35
  flwr/client/message_handler/__init__.py,sha256=abHvBRJJiiaAMNgeILQbMOa6h8WqMK2BcnvxwQZFpic,719
33
- flwr/client/message_handler/message_handler.py,sha256=369gEm8t1Tbp_Y74XlOGMy_mvD1zawD-dLwsBL174tY,6594
36
+ flwr/client/message_handler/message_handler.py,sha256=SlIU-l6GgB3wfA1Qq2x7z1SSSCXO4SO4pM122QNyTvU,6516
34
37
  flwr/client/message_handler/task_handler.py,sha256=ZDJBKmrn2grRMNl1rU1iGs7FiMHL5VmZiSp_6h9GHVU,1824
35
38
  flwr/client/mod/__init__.py,sha256=w6r7n6fWIrrm4lEk36lh9f1Ix6LXgAzQUrgjmMspY98,961
36
- flwr/client/mod/centraldp_mods.py,sha256=aHbzGjSbyRENuU5vzad_tkJ9UDb48uHEvUq-zgydBwo,4954
39
+ flwr/client/mod/centraldp_mods.py,sha256=Km-u3NJNxkhTLGDYoqMj31BXJrD_mozUH0DMicyKgI8,4953
37
40
  flwr/client/mod/secure_aggregation/__init__.py,sha256=AzCdezuzX2BfXUuxVRwXdv8-zUIXoU-Bf6u4LRhzvg8,796
38
- flwr/client/mod/secure_aggregation/secaggplus_mod.py,sha256=Zc5b2C58SYeyQVXIbLRESOIq4rMUOuzMHNAjdSAPt6I,19434
41
+ flwr/client/mod/secure_aggregation/secaggplus_mod.py,sha256=XKSxUdozUVcuvEaOV5syEnLr6B7ZlULk_ugh8SOdRWQ,19430
39
42
  flwr/client/mod/utils.py,sha256=lvETHcCYsSWz7h8I772hCV_kZspxqlMqzriMZ-SxmKc,1226
40
43
  flwr/client/node_state.py,sha256=KTTs_l4I0jBM7IsSsbAGjhfL_yZC3QANbzyvyfZBRDM,1778
41
44
  flwr/client/node_state_tests.py,sha256=gPwz0zf2iuDSa11jedkur_u3Xm7lokIDG5ALD2MCvSw,2195
@@ -43,9 +46,9 @@ flwr/client/numpy_client.py,sha256=u76GWAdHmJM88Agm2EgLQSvO8Jnk225mJTk-_TmPjFE,1
43
46
  flwr/client/rest_client/__init__.py,sha256=ThwOnkMdzxo_UuyTI47Q7y9oSpuTgNT2OuFvJCfuDiw,735
44
47
  flwr/client/rest_client/connection.py,sha256=WGkml4gmrbbJ6OAmW3jARmL5nGaOoXo7JjWQbhPhizM,12422
45
48
  flwr/client/typing.py,sha256=c9EvjlEjasxn1Wqx6bGl6Xg6vM1gMFfmXht-E2i5J-k,1006
46
- flwr/common/__init__.py,sha256=_P_A76POYbCgtUc0lclwnVJb2XL--p6kzC9zs_COdRY,3583
49
+ flwr/common/__init__.py,sha256=2dhte32YQNGHyxUazJcZuVBT433KgUkJA0FMQ4PBWjQ,3669
47
50
  flwr/common/address.py,sha256=iTAN9jtmIGMrWFnx9XZQl45ZEtQJVZZLYPRBSNVARGI,1882
48
- flwr/common/constant.py,sha256=pt7_M0AEwv7DU8etLkDmn4MqKxHNYNk4HtjuvitJbJE,1806
51
+ flwr/common/constant.py,sha256=Qh7tsgaqUwU76jnrAa9mbnuvom4ncAa0e6Xsq9jgpBU,1934
49
52
  flwr/common/context.py,sha256=ounF-mWPPtXGwtae3sg5EhF58ScviOa3MVqxRpGVu-8,1313
50
53
  flwr/common/date.py,sha256=UWhBZj49yX9LD4BmatS_ZFZu_-kweGh0KQJ1djyWWH4,891
51
54
  flwr/common/differential_privacy.py,sha256=k9XuH8ZGp3bZtfJbydviCDcjImvcocPtjqLFJLJTgGs,5656
@@ -114,7 +117,7 @@ flwr/server/client_proxy.py,sha256=4G-oTwhb45sfWLx2uZdcXD98IZwdTS6F88xe3akCdUg,2
114
117
  flwr/server/compat/__init__.py,sha256=VxnJtJyOjNFQXMNi9hIuzNlZM5n0Hj1p3aq_Pm2udw4,892
115
118
  flwr/server/compat/app.py,sha256=2WwkcXJt6HFS1NI9TGnl4CSECMo_VnDQ126vtdDciTs,5288
116
119
  flwr/server/compat/app_utils.py,sha256=-Ey5fyRpovmp4nHglVbliITcbxzxX_0qdtZwwfMS4ZI,3450
117
- flwr/server/compat/driver_client_proxy.py,sha256=XqKaDH850rqTHeQBcJAZvKnxSScLRT4l3rzf6R2Mt5w,6478
120
+ flwr/server/compat/driver_client_proxy.py,sha256=yOgaKZ6jlvqgewtjM8F906Rik8xqerxMptAhEzevWA0,6368
118
121
  flwr/server/compat/legacy_context.py,sha256=D2s7PvQoDnTexuRmf1uG9Von7GUj4Qqyr7qLklSlKAM,1766
119
122
  flwr/server/criterion.py,sha256=ypbAexbztzGUxNen9RCHF91QeqiEQix4t4Ih3E-42MM,1061
120
123
  flwr/server/driver/__init__.py,sha256=yYyVX1FcDiDFM6rw0-DSZpuRy0EoWRfG9puwlQUswFA,820
@@ -180,16 +183,16 @@ flwr/server/utils/__init__.py,sha256=RQVbo-bcsVtp_lJBf7dL5w01FbLrr7v3YedeGp5_YMs
180
183
  flwr/server/utils/tensorboard.py,sha256=k0G6bqsLx7wfYbH2KtXsDYcOCfyIeE12-hefXA7lZdg,5485
181
184
  flwr/server/utils/validator.py,sha256=IJN2475yyD_i_9kg_SJ_JodIuZh58ufpWGUDQRAqu2s,4740
182
185
  flwr/server/workflow/__init__.py,sha256=2YrKq5wUwge8Tm1xaAdf2P3l4LbM4olka6tO_0_Mu9A,787
183
- flwr/server/workflow/default_workflows.py,sha256=DKSt14WY5m19ujwh6UDP4a31kRs-j6V_NZm5cXn01ZY,12705
186
+ flwr/server/workflow/default_workflows.py,sha256=G-5ziRasLHX2iPpAUg0CtmQ5Pb-x-5-lZAleIlem048,12655
184
187
  flwr/simulation/__init__.py,sha256=hpoKzdovrH0_Cf8HIcXxQxyUUb3BiSk-WUNLf5STHcc,1400
185
188
  flwr/simulation/app.py,sha256=WqJxdXTEuehwMW605p5NMmvBbKYx5tuqnV3Mp7jSWXM,13904
186
189
  flwr/simulation/ray_transport/__init__.py,sha256=FsaAnzC4cw4DqoouBCix6496k29jACkfeIam55BvW9g,734
187
190
  flwr/simulation/ray_transport/ray_actor.py,sha256=zRETW_xuCAOLRFaYnQ-q3IBSz0LIv_0RifGuhgWaYOg,19872
188
- flwr/simulation/ray_transport/ray_client_proxy.py,sha256=DpmrBC87_sX3J4WrrwzyEDIjONUeliBZx9T-gZGuPmQ,6799
191
+ flwr/simulation/ray_transport/ray_client_proxy.py,sha256=L49gtigsf4vTQgRiqzOgcPEuS_l-EuTj29Ohw6ekbSI,6721
189
192
  flwr/simulation/ray_transport/utils.py,sha256=TYdtfg1P9VfTdLMOJlifInGpxWHYs9UfUqIv2wfkRLA,2392
190
193
  flwr/simulation/run_simulation.py,sha256=1JMP5nYFeGrZzcpw_Q0aDvyla2AvRz5aFJw1i1InSvs,15681
191
- flwr_nightly-1.8.0.dev20240305.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
192
- flwr_nightly-1.8.0.dev20240305.dist-info/METADATA,sha256=n9LsaIZyEjRRAc4g-J9pCsAy1ELgHrw38nDI8n5QPTM,15184
193
- flwr_nightly-1.8.0.dev20240305.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
194
- flwr_nightly-1.8.0.dev20240305.dist-info/entry_points.txt,sha256=utu2wybGyYJSTtsB2ktY_gmy-XtMFo9EFZdishX0zR4,320
195
- flwr_nightly-1.8.0.dev20240305.dist-info/RECORD,,
194
+ flwr_nightly-1.8.0.dev20240306.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
195
+ flwr_nightly-1.8.0.dev20240306.dist-info/METADATA,sha256=wo7hfHBsAblM1fzIAQtlRqKBJoJHOoawPiKcnJSVwZA,15257
196
+ flwr_nightly-1.8.0.dev20240306.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
197
+ flwr_nightly-1.8.0.dev20240306.dist-info/entry_points.txt,sha256=utu2wybGyYJSTtsB2ktY_gmy-XtMFo9EFZdishX0zR4,320
198
+ flwr_nightly-1.8.0.dev20240306.dist-info/RECORD,,