flwr-nightly 1.8.0.dev20240305__py3-none-any.whl → 1.8.0.dev20240306__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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,,