flwr-nightly 1.7.0.dev20240124__py3-none-any.whl → 1.7.0.dev20240126__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
flwr/client/app.py CHANGED
@@ -352,7 +352,7 @@ def _start_client_internal(
352
352
  break
353
353
 
354
354
  # Register state
355
- node_state.register_runstate(run_id=task_ins.run_id)
355
+ node_state.register_context(run_id=task_ins.run_id)
356
356
 
357
357
  # Load app
358
358
  app: Flower = load_flower_callable_fn()
@@ -360,14 +360,14 @@ def _start_client_internal(
360
360
  # Handle task message
361
361
  fwd_msg: Fwd = Fwd(
362
362
  task_ins=task_ins,
363
- state=node_state.retrieve_runstate(run_id=task_ins.run_id),
363
+ context=node_state.retrieve_context(run_id=task_ins.run_id),
364
364
  )
365
365
  bwd_msg: Bwd = app(fwd=fwd_msg)
366
366
 
367
367
  # Update node state
368
- node_state.update_runstate(
369
- run_id=bwd_msg.task_res.run_id,
370
- run_state=bwd_msg.state,
368
+ node_state.update_context(
369
+ run_id=fwd_msg.task_ins.run_id,
370
+ context=bwd_msg.context,
371
371
  )
372
372
 
373
373
  # Send
flwr/client/client.py CHANGED
@@ -19,7 +19,6 @@ from __future__ import annotations
19
19
 
20
20
  from abc import ABC
21
21
 
22
- from flwr.client.run_state import RunState
23
22
  from flwr.common import (
24
23
  Code,
25
24
  EvaluateIns,
@@ -33,12 +32,13 @@ from flwr.common import (
33
32
  Parameters,
34
33
  Status,
35
34
  )
35
+ from flwr.common.context import Context
36
36
 
37
37
 
38
38
  class Client(ABC):
39
39
  """Abstract base class for Flower clients."""
40
40
 
41
- state: RunState
41
+ context: Context
42
42
 
43
43
  def get_properties(self, ins: GetPropertiesIns) -> GetPropertiesRes:
44
44
  """Return set of client's properties.
@@ -141,13 +141,13 @@ class Client(ABC):
141
141
  metrics={},
142
142
  )
143
143
 
144
- def get_state(self) -> RunState:
145
- """Get the run state from this client."""
146
- return self.state
144
+ def get_context(self) -> Context:
145
+ """Get the run context from this client."""
146
+ return self.context
147
147
 
148
- def set_state(self, state: RunState) -> None:
149
- """Apply a run state to this client."""
150
- self.state = state
148
+ def set_context(self, context: Context) -> None:
149
+ """Apply a run context to this client."""
150
+ self.context = context
151
151
 
152
152
  def to_client(self) -> Client:
153
153
  """Return client (itself)."""
flwr/client/flower.py CHANGED
@@ -56,12 +56,12 @@ class Flower:
56
56
  ) -> None:
57
57
  # Create wrapper function for `handle`
58
58
  def ffn(fwd: Fwd) -> Bwd: # pylint: disable=invalid-name
59
- task_res, state_updated = handle(
59
+ task_res, context_updated = handle(
60
60
  client_fn=client_fn,
61
- state=fwd.state,
61
+ context=fwd.context,
62
62
  task_ins=fwd.task_ins,
63
63
  )
64
- return Bwd(task_res=task_res, state=state_updated)
64
+ return Bwd(task_res=task_res, context=context_updated)
65
65
 
66
66
  # Wrap middleware layers around the wrapped handle function
67
67
  self._call = make_ffn(ffn, layers if layers is not None else [])
@@ -28,10 +28,10 @@ from flwr.client.message_handler.task_handler import (
28
28
  get_server_message_from_task_ins,
29
29
  wrap_client_message_in_task_res,
30
30
  )
31
- from flwr.client.run_state import RunState
32
31
  from flwr.client.secure_aggregation import SecureAggregationHandler
33
32
  from flwr.client.typing import ClientFn
34
33
  from flwr.common import serde
34
+ from flwr.common.context import Context
35
35
  from flwr.proto.task_pb2 import ( # pylint: disable=E0611
36
36
  SecureAggregation,
37
37
  Task,
@@ -88,16 +88,16 @@ def handle_control_message(task_ins: TaskIns) -> Tuple[Optional[TaskRes], int]:
88
88
 
89
89
 
90
90
  def handle(
91
- client_fn: ClientFn, state: RunState, task_ins: TaskIns
92
- ) -> Tuple[TaskRes, RunState]:
91
+ client_fn: ClientFn, context: Context, task_ins: TaskIns
92
+ ) -> Tuple[TaskRes, Context]:
93
93
  """Handle incoming TaskIns from the server.
94
94
 
95
95
  Parameters
96
96
  ----------
97
97
  client_fn : ClientFn
98
98
  A callable that instantiates a Client.
99
- state : RunState
100
- A dataclass storing the state for the run being executed by the client.
99
+ context : Context
100
+ A dataclass storing the context for the run being executed by the client.
101
101
  task_ins: TaskIns
102
102
  The task instruction coming from the server, to be processed by the client.
103
103
 
@@ -110,7 +110,7 @@ def handle(
110
110
  if server_msg is None:
111
111
  # Instantiate the client
112
112
  client = client_fn("-1")
113
- client.set_state(state)
113
+ client.set_context(context)
114
114
  # Secure Aggregation
115
115
  if task_ins.task.HasField("sa") and isinstance(
116
116
  client, SecureAggregationHandler
@@ -127,24 +127,24 @@ def handle(
127
127
  sa=SecureAggregation(named_values=serde.named_values_to_proto(res)),
128
128
  ),
129
129
  )
130
- return task_res, client.get_state()
130
+ return task_res, client.get_context()
131
131
  raise NotImplementedError()
132
- client_msg, updated_state = handle_legacy_message(client_fn, state, server_msg)
132
+ client_msg, updated_context = handle_legacy_message(client_fn, context, server_msg)
133
133
  task_res = wrap_client_message_in_task_res(client_msg)
134
- return task_res, updated_state
134
+ return task_res, updated_context
135
135
 
136
136
 
137
137
  def handle_legacy_message(
138
- client_fn: ClientFn, state: RunState, server_msg: ServerMessage
139
- ) -> Tuple[ClientMessage, RunState]:
138
+ client_fn: ClientFn, context: Context, server_msg: ServerMessage
139
+ ) -> Tuple[ClientMessage, Context]:
140
140
  """Handle incoming messages from the server.
141
141
 
142
142
  Parameters
143
143
  ----------
144
144
  client_fn : ClientFn
145
145
  A callable that instantiates a Client.
146
- state : RunState
147
- A dataclass storing the state for the run being executed by the client.
146
+ context : Context
147
+ A dataclass storing the context for the run being executed by the client.
148
148
  server_msg: ServerMessage
149
149
  The message coming from the server, to be processed by the client.
150
150
 
@@ -161,7 +161,7 @@ def handle_legacy_message(
161
161
 
162
162
  # Instantiate the client
163
163
  client = client_fn("-1")
164
- client.set_state(state)
164
+ client.set_context(context)
165
165
  # Execute task
166
166
  message = None
167
167
  if field == "get_properties_ins":
@@ -173,7 +173,7 @@ def handle_legacy_message(
173
173
  if field == "evaluate_ins":
174
174
  message = _evaluate(client, server_msg.evaluate_ins)
175
175
  if message:
176
- return message, client.get_state()
176
+ return message, client.get_context()
177
177
  raise UnknownServerMessage()
178
178
 
179
179
 
flwr/client/node_state.py CHANGED
@@ -17,7 +17,8 @@
17
17
 
18
18
  from typing import Any, Dict
19
19
 
20
- from flwr.client.run_state import RunState
20
+ from flwr.common.context import Context
21
+ from flwr.common.recordset import RecordSet
21
22
 
22
23
 
23
24
  class NodeState:
@@ -25,24 +26,24 @@ class NodeState:
25
26
 
26
27
  def __init__(self) -> None:
27
28
  self._meta: Dict[str, Any] = {} # holds metadata about the node
28
- self.run_states: Dict[int, RunState] = {}
29
+ self.run_contexts: Dict[int, Context] = {}
29
30
 
30
- def register_runstate(self, run_id: int) -> None:
31
- """Register new run state for this node."""
32
- if run_id not in self.run_states:
33
- self.run_states[run_id] = RunState({})
31
+ def register_context(self, run_id: int) -> None:
32
+ """Register new run context for this node."""
33
+ if run_id not in self.run_contexts:
34
+ self.run_contexts[run_id] = Context(state=RecordSet())
34
35
 
35
- def retrieve_runstate(self, run_id: int) -> RunState:
36
- """Get run state given a run_id."""
37
- if run_id in self.run_states:
38
- return self.run_states[run_id]
36
+ def retrieve_context(self, run_id: int) -> Context:
37
+ """Get run context given a run_id."""
38
+ if run_id in self.run_contexts:
39
+ return self.run_contexts[run_id]
39
40
 
40
41
  raise RuntimeError(
41
- f"RunState for run_id={run_id} doesn't exist."
42
- " A run must be registered before it can be retrieved or updated "
42
+ f"Context for run_id={run_id} doesn't exist."
43
+ " A run context must be registered before it can be retrieved or updated "
43
44
  " by a client."
44
45
  )
45
46
 
46
- def update_runstate(self, run_id: int, run_state: RunState) -> None:
47
- """Update run state."""
48
- self.run_states[run_id] = run_state
47
+ def update_context(self, run_id: int, context: Context) -> None:
48
+ """Update run context."""
49
+ self.run_contexts[run_id] = context
@@ -16,17 +16,22 @@
16
16
 
17
17
 
18
18
  from flwr.client.node_state import NodeState
19
- from flwr.client.run_state import RunState
19
+ from flwr.common.configsrecord import ConfigsRecord
20
+ from flwr.common.context import Context
20
21
  from flwr.proto.task_pb2 import TaskIns # pylint: disable=E0611
21
22
 
22
23
 
23
- def _run_dummy_task(state: RunState) -> RunState:
24
- if "counter" in state.state:
25
- state.state["counter"] += "1"
26
- else:
27
- state.state["counter"] = "1"
24
+ def _run_dummy_task(context: Context) -> Context:
25
+ counter_value: str = "1"
26
+ if "counter" in context.state.configs.keys():
27
+ counter_value = context.get_configs("counter")["count"] # type: ignore
28
+ counter_value += "1"
28
29
 
29
- return state
30
+ context.state.set_configs(
31
+ name="counter", record=ConfigsRecord({"count": counter_value})
32
+ )
33
+
34
+ return context
30
35
 
31
36
 
32
37
  def test_multirun_in_node_state() -> None:
@@ -43,17 +48,17 @@ def test_multirun_in_node_state() -> None:
43
48
  run_id = task.run_id
44
49
 
45
50
  # Register
46
- node_state.register_runstate(run_id=run_id)
51
+ node_state.register_context(run_id=run_id)
47
52
 
48
53
  # Get run state
49
- state = node_state.retrieve_runstate(run_id=run_id)
54
+ context = node_state.retrieve_context(run_id=run_id)
50
55
 
51
56
  # Run "task"
52
- updated_state = _run_dummy_task(state)
57
+ updated_state = _run_dummy_task(context)
53
58
 
54
59
  # Update run state
55
- node_state.update_runstate(run_id=run_id, run_state=updated_state)
60
+ node_state.update_context(run_id=run_id, context=updated_state)
56
61
 
57
62
  # Verify values
58
- for run_id, state in node_state.run_states.items():
59
- assert state.state["counter"] == expected_values[run_id]
63
+ for run_id, context in node_state.run_contexts.items():
64
+ assert context.state.get_configs("counter")["count"] == expected_values[run_id]
@@ -19,7 +19,6 @@ from abc import ABC
19
19
  from typing import Callable, Dict, Tuple
20
20
 
21
21
  from flwr.client.client import Client
22
- from flwr.client.run_state import RunState
23
22
  from flwr.common import (
24
23
  Config,
25
24
  NDArrays,
@@ -27,6 +26,7 @@ from flwr.common import (
27
26
  ndarrays_to_parameters,
28
27
  parameters_to_ndarrays,
29
28
  )
29
+ from flwr.common.context import Context
30
30
  from flwr.common.typing import (
31
31
  Code,
32
32
  EvaluateIns,
@@ -70,7 +70,7 @@ Example
70
70
  class NumPyClient(ABC):
71
71
  """Abstract base class for Flower clients using NumPy."""
72
72
 
73
- state: RunState
73
+ context: Context
74
74
 
75
75
  def get_properties(self, config: Config) -> Dict[str, Scalar]:
76
76
  """Return a client's set of properties.
@@ -174,13 +174,13 @@ class NumPyClient(ABC):
174
174
  _ = (self, parameters, config)
175
175
  return 0.0, 0, {}
176
176
 
177
- def get_state(self) -> RunState:
178
- """Get the run state from this client."""
179
- return self.state
177
+ def get_context(self) -> Context:
178
+ """Get the run context from this client."""
179
+ return self.context
180
180
 
181
- def set_state(self, state: RunState) -> None:
182
- """Apply a run state to this client."""
183
- self.state = state
181
+ def set_context(self, context: Context) -> None:
182
+ """Apply a run context to this client."""
183
+ self.context = context
184
184
 
185
185
  def to_client(self) -> Client:
186
186
  """Convert to object to Client type and return it."""
@@ -278,21 +278,21 @@ def _evaluate(self: Client, ins: EvaluateIns) -> EvaluateRes:
278
278
  )
279
279
 
280
280
 
281
- def _get_state(self: Client) -> RunState:
282
- """Return state of underlying NumPyClient."""
283
- return self.numpy_client.get_state() # type: ignore
281
+ def _get_context(self: Client) -> Context:
282
+ """Return context of underlying NumPyClient."""
283
+ return self.numpy_client.get_context() # type: ignore
284
284
 
285
285
 
286
- def _set_state(self: Client, state: RunState) -> None:
287
- """Apply state to underlying NumPyClient."""
288
- self.numpy_client.set_state(state) # type: ignore
286
+ def _set_context(self: Client, context: Context) -> None:
287
+ """Apply context to underlying NumPyClient."""
288
+ self.numpy_client.set_context(context) # type: ignore
289
289
 
290
290
 
291
291
  def _wrap_numpy_client(client: NumPyClient) -> Client:
292
292
  member_dict: Dict[str, Callable] = { # type: ignore
293
293
  "__init__": _constructor,
294
- "get_state": _get_state,
295
- "set_state": _set_state,
294
+ "get_context": _get_context,
295
+ "set_context": _set_context,
296
296
  }
297
297
 
298
298
  # Add wrapper type methods (if overridden)
flwr/client/typing.py CHANGED
@@ -17,7 +17,7 @@
17
17
  from dataclasses import dataclass
18
18
  from typing import Callable
19
19
 
20
- from flwr.client.run_state import RunState
20
+ from flwr.common.context import Context
21
21
  from flwr.proto.task_pb2 import TaskIns, TaskRes # pylint: disable=E0611
22
22
 
23
23
  from .client import Client as Client
@@ -28,7 +28,7 @@ class Fwd:
28
28
  """."""
29
29
 
30
30
  task_ins: TaskIns
31
- state: RunState
31
+ context: Context
32
32
 
33
33
 
34
34
  @dataclass
@@ -36,7 +36,7 @@ class Bwd:
36
36
  """."""
37
37
 
38
38
  task_res: TaskRes
39
- state: RunState
39
+ context: Context
40
40
 
41
41
 
42
42
  FlowerCallable = Callable[[Fwd], Bwd]
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -12,14 +12,27 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  # ==============================================================================
15
- """Run state."""
15
+ """Context."""
16
+
16
17
 
17
18
  from dataclasses import dataclass
18
- from typing import Dict
19
+
20
+ from .recordset import RecordSet
19
21
 
20
22
 
21
23
  @dataclass
22
- class RunState:
23
- """State of a run executed by a client node."""
24
+ class Context:
25
+ """State of your run.
26
+
27
+ Parameters
28
+ ----------
29
+ state : RecordSet
30
+ Holds records added by the entity in a given run and that will stay local.
31
+ This means that the data it holds will never leave the system it's running from.
32
+ This can be used as an intermediate storage or scratchpad when
33
+ executing middleware layers. It can also be used as a memory to access
34
+ at different points during the lifecycle of this entity (e.g. across
35
+ multiple rounds)
36
+ """
24
37
 
25
- state: Dict[str, str]
38
+ state: RecordSet
@@ -12,7 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  # ==============================================================================
15
- """FlowerContext and Metadata."""
15
+ """Message."""
16
16
 
17
17
 
18
18
  from dataclasses import dataclass
@@ -48,30 +48,17 @@ class Metadata:
48
48
 
49
49
 
50
50
  @dataclass
51
- class FlowerContext:
51
+ class Message:
52
52
  """State of your application from the viewpoint of the entity using it.
53
53
 
54
54
  Parameters
55
55
  ----------
56
- in_message : RecordSet
57
- Holds records sent by another entity (e.g. sent by the server-side
58
- logic to a client, or vice-versa)
59
- out_message : RecordSet
60
- Holds records added by the current entity. This `RecordSet` will
61
- be sent out (e.g. back to the server-side for aggregation of
62
- parameter, or to the client to perform a certain task)
63
- local : RecordSet
64
- Holds record added by the current entity and that will stay local.
65
- This means that the data it holds will never leave the system it's running from.
66
- This can be used as an intermediate storage or scratchpad when
67
- executing middleware layers. It can also be used as a memory to access
68
- at different points during the lifecycle of this entity (e.g. across
69
- multiple rounds)
70
56
  metadata : Metadata
71
57
  A dataclass including information about the task to be executed.
58
+ message : RecordSet
59
+ Holds records either sent by another entity (e.g. sent by the server-side
60
+ logic to a client, or vice-versa) or that will be sent to it.
72
61
  """
73
62
 
74
- in_message: RecordSet
75
- out_message: RecordSet
76
- local: RecordSet
77
63
  metadata: Metadata
64
+ message: RecordSet
@@ -40,20 +40,22 @@ from .typing import (
40
40
 
41
41
 
42
42
  def parametersrecord_to_parameters(
43
- record: ParametersRecord, keep_input: bool = False
43
+ record: ParametersRecord, keep_input: bool
44
44
  ) -> Parameters:
45
45
  """Convert ParameterRecord to legacy Parameters.
46
46
 
47
- Warning: Because `Arrays` in `ParametersRecord` encode more information of the
47
+ Warnings
48
+ --------
49
+ Because `Arrays` in `ParametersRecord` encode more information of the
48
50
  array-like or tensor-like data (e.g their datatype, shape) than `Parameters` it
49
51
  might not be possible to reconstruct such data structures from `Parameters` objects
50
- alone. Additional information or metadta must be provided from elsewhere.
52
+ alone. Additional information or metadata must be provided from elsewhere.
51
53
 
52
54
  Parameters
53
55
  ----------
54
56
  record : ParametersRecord
55
57
  The record to be conveted into Parameters.
56
- keep_input : bool (default: False)
58
+ keep_input : bool
57
59
  A boolean indicating whether entries in the record should be deleted from the
58
60
  input dictionary immediately after adding them to the record.
59
61
  """
@@ -74,7 +76,7 @@ def parametersrecord_to_parameters(
74
76
 
75
77
 
76
78
  def parameters_to_parametersrecord(
77
- parameters: Parameters, keep_input: bool = False
79
+ parameters: Parameters, keep_input: bool
78
80
  ) -> ParametersRecord:
79
81
  """Convert legacy Parameters into a single ParametersRecord.
80
82
 
@@ -86,7 +88,7 @@ def parameters_to_parametersrecord(
86
88
  ----------
87
89
  parameters : Parameters
88
90
  Parameters object to be represented as a ParametersRecord.
89
- keep_input : bool (default: False)
91
+ keep_input : bool
90
92
  A boolean indicating whether parameters should be deleted from the input
91
93
  Parameters object (i.e. a list of serialized NumPy arrays) immediately after
92
94
  adding them to the record.
@@ -96,17 +98,17 @@ def parameters_to_parametersrecord(
96
98
  p_record = ParametersRecord()
97
99
 
98
100
  num_arrays = len(parameters.tensors)
101
+ ordered_dict = OrderedDict()
99
102
  for idx in range(num_arrays):
100
103
  if keep_input:
101
104
  tensor = parameters.tensors[idx]
102
105
  else:
103
106
  tensor = parameters.tensors.pop(0)
104
- p_record.set_parameters(
105
- OrderedDict(
106
- {str(idx): Array(data=tensor, dtype="", stype=tensor_type, shape=[])}
107
- )
107
+ ordered_dict[str(idx)] = Array(
108
+ data=tensor, dtype="", stype=tensor_type, shape=[]
108
109
  )
109
110
 
111
+ p_record.set_parameters(ordered_dict, keep_input=keep_input)
110
112
  return p_record
111
113
 
112
114
 
@@ -330,11 +332,15 @@ def getparametersins_to_recordset(getparameters_ins: GetParametersIns) -> Record
330
332
  return recordset
331
333
 
332
334
 
333
- def getparametersres_to_recordset(getparametersres: GetParametersRes) -> RecordSet:
335
+ def getparametersres_to_recordset(
336
+ getparametersres: GetParametersRes, keep_input: bool
337
+ ) -> RecordSet:
334
338
  """Construct a RecordSet from a GetParametersRes object."""
335
339
  recordset = RecordSet()
336
340
  res_str = "getparametersres"
337
- parameters_record = parameters_to_parametersrecord(getparametersres.parameters)
341
+ parameters_record = parameters_to_parametersrecord(
342
+ getparametersres.parameters, keep_input=keep_input
343
+ )
338
344
  recordset.set_parameters(f"{res_str}.parameters", parameters_record)
339
345
 
340
346
  # status
@@ -345,11 +351,13 @@ def getparametersres_to_recordset(getparametersres: GetParametersRes) -> RecordS
345
351
  return recordset
346
352
 
347
353
 
348
- def recordset_to_getparametersres(recordset: RecordSet) -> GetParametersRes:
354
+ def recordset_to_getparametersres(
355
+ recordset: RecordSet, keep_input: bool
356
+ ) -> GetParametersRes:
349
357
  """Derive GetParametersRes from a RecordSet object."""
350
358
  res_str = "getparametersres"
351
359
  parameters = parametersrecord_to_parameters(
352
- recordset.get_parameters(f"{res_str}.parameters")
360
+ recordset.get_parameters(f"{res_str}.parameters"), keep_input=keep_input
353
361
  )
354
362
 
355
363
  status = _extract_status_from_recordset(res_str, recordset)
flwr/common/serde.py CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  from typing import Any, Dict, List, MutableMapping, OrderedDict, Type, TypeVar, cast
19
19
 
20
- from google.protobuf.message import Message
20
+ from google.protobuf.message import Message as GrpcMessage
21
21
 
22
22
  # pylint: disable=E0611
23
23
  from flwr.proto.recordset_pb2 import Array as ProtoArray
@@ -30,7 +30,7 @@ from flwr.proto.recordset_pb2 import MetricsRecordValue as ProtoMetricsRecordVal
30
30
  from flwr.proto.recordset_pb2 import ParametersRecord as ProtoParametersRecord
31
31
  from flwr.proto.recordset_pb2 import RecordSet as ProtoRecordSet
32
32
  from flwr.proto.recordset_pb2 import Sint64List, StringList
33
- from flwr.proto.task_pb2 import Value
33
+ from flwr.proto.task_pb2 import Task, TaskIns, TaskRes, Value
34
34
  from flwr.proto.transport_pb2 import (
35
35
  ClientMessage,
36
36
  Code,
@@ -44,6 +44,7 @@ from flwr.proto.transport_pb2 import (
44
44
  # pylint: enable=E0611
45
45
  from . import typing
46
46
  from .configsrecord import ConfigsRecord
47
+ from .message import Message, Metadata
47
48
  from .metricsrecord import MetricsRecord
48
49
  from .parametersrecord import Array, ParametersRecord
49
50
  from .recordset import RecordSet
@@ -600,12 +601,15 @@ T = TypeVar("T")
600
601
  def _record_value_to_proto(
601
602
  value: Any, allowed_types: List[type], proto_class: Type[T]
602
603
  ) -> T:
603
- """Serialize `*RecordValue` to ProtoBuf."""
604
+ """Serialize `*RecordValue` to ProtoBuf.
605
+
606
+ Note: `bool` MUST be put in the front of allowd_types if it exists.
607
+ """
604
608
  arg = {}
605
609
  for t in allowed_types:
606
610
  # Single element
607
611
  # Note: `isinstance(False, int) == True`.
608
- if type(value) == t: # pylint: disable=C0123
612
+ if isinstance(value, t):
609
613
  arg[_type_to_field[t]] = value
610
614
  return proto_class(**arg)
611
615
  # List
@@ -620,7 +624,7 @@ def _record_value_to_proto(
620
624
  )
621
625
 
622
626
 
623
- def _record_value_from_proto(value_proto: Message) -> Any:
627
+ def _record_value_from_proto(value_proto: GrpcMessage) -> Any:
624
628
  """Deserialize `*RecordValue` from ProtoBuf."""
625
629
  value_field = cast(str, value_proto.WhichOneof("value"))
626
630
  if value_field.endswith("list"):
@@ -633,7 +637,14 @@ def _record_value_from_proto(value_proto: Message) -> Any:
633
637
  def _record_value_dict_to_proto(
634
638
  value_dict: Dict[str, Any], allowed_types: List[type], value_proto_class: Type[T]
635
639
  ) -> Dict[str, T]:
636
- """Serialize the record value dict to ProtoBuf."""
640
+ """Serialize the record value dict to ProtoBuf.
641
+
642
+ Note: `bool` MUST be put in the front of allowd_types if it exists.
643
+ """
644
+ # Move bool to the front
645
+ if bool in allowed_types and allowed_types[0] != bool:
646
+ allowed_types.remove(bool)
647
+ allowed_types.insert(0, bool)
637
648
 
638
649
  def proto(_v: Any) -> T:
639
650
  return _record_value_to_proto(_v, allowed_types, value_proto_class)
@@ -707,7 +718,7 @@ def configs_record_to_proto(record: ConfigsRecord) -> ProtoConfigsRecord:
707
718
  """Serialize ConfigsRecord to ProtoBuf."""
708
719
  return ProtoConfigsRecord(
709
720
  data=_record_value_dict_to_proto(
710
- record.data, [int, float, bool, str, bytes], ProtoConfigsRecordValue
721
+ record.data, [bool, int, float, str, bytes], ProtoConfigsRecordValue
711
722
  )
712
723
  )
713
724
 
@@ -751,3 +762,64 @@ def recordset_from_proto(recordset_proto: ProtoRecordSet) -> RecordSet:
751
762
  k: configs_record_from_proto(v) for k, v in recordset_proto.configs.items()
752
763
  },
753
764
  )
765
+
766
+
767
+ # === Message ===
768
+
769
+
770
+ def message_to_taskins(message: Message) -> TaskIns:
771
+ """Create a TaskIns from the Message."""
772
+ return TaskIns(
773
+ task=Task(
774
+ ttl=message.metadata.ttl,
775
+ task_type=message.metadata.task_type,
776
+ recordset=recordset_to_proto(message.message),
777
+ ),
778
+ )
779
+
780
+
781
+ def message_from_taskins(taskins: TaskIns) -> Message:
782
+ """Create a Message from the TaskIns."""
783
+ # Retrieve the Metadata
784
+ metadata = Metadata(
785
+ run_id=taskins.run_id,
786
+ task_id=taskins.task_id,
787
+ group_id=taskins.group_id,
788
+ ttl=taskins.task.ttl,
789
+ task_type=taskins.task.task_type,
790
+ )
791
+
792
+ # Return the Message
793
+ return Message(
794
+ metadata=metadata,
795
+ message=recordset_from_proto(taskins.task.recordset),
796
+ )
797
+
798
+
799
+ def message_to_taskres(message: Message) -> TaskRes:
800
+ """Create a TaskRes from the Message."""
801
+ return TaskRes(
802
+ task=Task(
803
+ ttl=message.metadata.ttl,
804
+ task_type=message.metadata.task_type,
805
+ recordset=recordset_to_proto(message.message),
806
+ ),
807
+ )
808
+
809
+
810
+ def message_from_taskres(taskres: TaskRes) -> Message:
811
+ """Create a Message from the TaskIns."""
812
+ # Retrieve the MetaData
813
+ metadata = Metadata(
814
+ run_id=taskres.run_id,
815
+ task_id=taskres.task_id,
816
+ group_id=taskres.group_id,
817
+ ttl=taskres.task.ttl,
818
+ task_type=taskres.task.task_type,
819
+ )
820
+
821
+ # Return the Message
822
+ return Message(
823
+ metadata=metadata,
824
+ message=recordset_from_proto(taskres.task.recordset),
825
+ )
@@ -27,7 +27,7 @@ from ray.util.actor_pool import ActorPool
27
27
 
28
28
  from flwr import common
29
29
  from flwr.client import Client, ClientFn
30
- from flwr.client.run_state import RunState
30
+ from flwr.common.context import Context
31
31
  from flwr.common.logger import log
32
32
  from flwr.simulation.ray_transport.utils import check_clientfn_returns_client
33
33
 
@@ -61,8 +61,8 @@ class VirtualClientEngineActor(ABC):
61
61
  client_fn: ClientFn,
62
62
  job_fn: JobFn,
63
63
  cid: str,
64
- state: RunState,
65
- ) -> Tuple[str, ClientRes, RunState]:
64
+ context: Context,
65
+ ) -> Tuple[str, ClientRes, Context]:
66
66
  """Run a client run."""
67
67
  # Execute tasks and return result
68
68
  # return also cid which is needed to ensure results
@@ -70,12 +70,12 @@ class VirtualClientEngineActor(ABC):
70
70
  try:
71
71
  # Instantiate client (check 'Client' type is returned)
72
72
  client = check_clientfn_returns_client(client_fn(cid))
73
- # Inject state
74
- client.set_state(state)
73
+ # Inject context
74
+ client.set_context(context)
75
75
  # Run client job
76
76
  job_results = job_fn(client)
77
- # Retrieve state (potentially updated)
78
- updated_state = client.get_state()
77
+ # Retrieve context (potentially updated)
78
+ updated_context = client.get_context()
79
79
  except Exception as ex:
80
80
  client_trace = traceback.format_exc()
81
81
  message = (
@@ -89,7 +89,7 @@ class VirtualClientEngineActor(ABC):
89
89
  )
90
90
  raise ClientException(str(message)) from ex
91
91
 
92
- return cid, job_results, updated_state
92
+ return cid, job_results, updated_context
93
93
 
94
94
 
95
95
  @ray.remote
@@ -237,16 +237,16 @@ class VirtualClientEngineActorPool(ActorPool):
237
237
  self._idle_actors.extend(new_actors)
238
238
  self.num_actors += num_actors
239
239
 
240
- def submit(self, fn: Any, value: Tuple[ClientFn, JobFn, str, RunState]) -> None:
240
+ def submit(self, fn: Any, value: Tuple[ClientFn, JobFn, str, Context]) -> None:
241
241
  """Take idle actor and assign it a client run.
242
242
 
243
243
  Submit a job to an actor by first removing it from the list of idle actors, then
244
244
  check if this actor was flagged to be removed from the pool
245
245
  """
246
- client_fn, job_fn, cid, state = value
246
+ client_fn, job_fn, cid, context = value
247
247
  actor = self._idle_actors.pop()
248
248
  if self._check_and_remove_actor_from_pool(actor):
249
- future = fn(actor, client_fn, job_fn, cid, state)
249
+ future = fn(actor, client_fn, job_fn, cid, context)
250
250
  future_key = tuple(future) if isinstance(future, List) else future
251
251
  self._future_to_actor[future_key] = (self._next_task_index, actor, cid)
252
252
  self._next_task_index += 1
@@ -255,7 +255,7 @@ class VirtualClientEngineActorPool(ActorPool):
255
255
  self._cid_to_future[cid]["future"] = future_key
256
256
 
257
257
  def submit_client_job(
258
- self, actor_fn: Any, job: Tuple[ClientFn, JobFn, str, RunState]
258
+ self, actor_fn: Any, job: Tuple[ClientFn, JobFn, str, Context]
259
259
  ) -> None:
260
260
  """Submit a job while tracking client ids."""
261
261
  _, _, cid, _ = job
@@ -295,17 +295,17 @@ class VirtualClientEngineActorPool(ActorPool):
295
295
 
296
296
  return self._cid_to_future[cid]["ready"] # type: ignore
297
297
 
298
- def _fetch_future_result(self, cid: str) -> Tuple[ClientRes, RunState]:
299
- """Fetch result and updated state for a VirtualClient from Object Store.
298
+ def _fetch_future_result(self, cid: str) -> Tuple[ClientRes, Context]:
299
+ """Fetch result and updated context for a VirtualClient from Object Store.
300
300
 
301
301
  The job submitted by the ClientProxy interfacing with client with cid=cid is
302
302
  ready. Here we fetch it from the object store and return.
303
303
  """
304
304
  try:
305
305
  future: ObjectRef[Any] = self._cid_to_future[cid]["future"] # type: ignore
306
- res_cid, res, updated_state = ray.get(
306
+ res_cid, res, updated_context = ray.get(
307
307
  future
308
- ) # type: (str, ClientRes, RunState)
308
+ ) # type: (str, ClientRes, Context)
309
309
  except ray.exceptions.RayActorError as ex:
310
310
  log(ERROR, ex)
311
311
  if hasattr(ex, "actor_id"):
@@ -322,7 +322,7 @@ class VirtualClientEngineActorPool(ActorPool):
322
322
  # Reset mapping
323
323
  self._reset_cid_to_future_dict(cid)
324
324
 
325
- return res, updated_state
325
+ return res, updated_context
326
326
 
327
327
  def _flag_actor_for_removal(self, actor_id_hex: str) -> None:
328
328
  """Flag actor that should be removed from pool."""
@@ -409,7 +409,7 @@ class VirtualClientEngineActorPool(ActorPool):
409
409
 
410
410
  def get_client_result(
411
411
  self, cid: str, timeout: Optional[float]
412
- ) -> Tuple[ClientRes, RunState]:
412
+ ) -> Tuple[ClientRes, Context]:
413
413
  """Get result from VirtualClient with specific cid."""
414
414
  # Loop until all jobs submitted to the pool are completed. Break early
415
415
  # if the result for the ClientProxy calling this method is ready
@@ -421,5 +421,5 @@ class VirtualClientEngineActorPool(ActorPool):
421
421
  break
422
422
 
423
423
  # Fetch result belonging to the VirtualClient calling this method
424
- # Return both result from tasks and (potentially) updated run state
424
+ # Return both result from tasks and (potentially) updated run context
425
425
  return self._fetch_future_result(cid)
@@ -138,20 +138,20 @@ class RayActorClientProxy(ClientProxy):
138
138
  run_id = 0
139
139
 
140
140
  # Register state
141
- self.proxy_state.register_runstate(run_id=run_id)
141
+ self.proxy_state.register_context(run_id=run_id)
142
142
 
143
143
  # Retrieve state
144
- state = self.proxy_state.retrieve_runstate(run_id=run_id)
144
+ state = self.proxy_state.retrieve_context(run_id=run_id)
145
145
 
146
146
  try:
147
147
  self.actor_pool.submit_client_job(
148
148
  lambda a, c_fn, j_fn, cid, state: a.run.remote(c_fn, j_fn, cid, state),
149
149
  (self.client_fn, job_fn, self.cid, state),
150
150
  )
151
- res, updated_state = self.actor_pool.get_client_result(self.cid, timeout)
151
+ res, updated_context = self.actor_pool.get_client_result(self.cid, timeout)
152
152
 
153
153
  # Update state
154
- self.proxy_state.update_runstate(run_id=run_id, run_state=updated_state)
154
+ self.proxy_state.update_context(run_id=run_id, context=updated_context)
155
155
 
156
156
  except Exception as ex:
157
157
  if self.actor_pool.num_actors == 0:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.7.0.dev20240124
3
+ Version: 1.7.0.dev20240126
4
4
  Summary: Flower: A Friendly Federated Learning Framework
5
5
  Home-page: https://flower.dev
6
6
  License: Apache-2.0
@@ -184,6 +184,7 @@ Other [examples](https://github.com/adap/flower/tree/main/examples):
184
184
  - [Advanced Flower with TensorFlow/Keras](https://github.com/adap/flower/tree/main/examples/advanced-tensorflow)
185
185
  - [Advanced Flower with PyTorch](https://github.com/adap/flower/tree/main/examples/advanced-pytorch)
186
186
  - 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))
187
+ - [Flower through Docker Compose and with Grafana dashboard](https://github.com/adap/flower/tree/main/examples/flower-via-docker-compose)
187
188
 
188
189
  ## Community
189
190
 
@@ -1,42 +1,42 @@
1
1
  flwr/__init__.py,sha256=6zbcS7z2q-VUdmpFppLH6BacsE-ZFmfq6OvtKNOyYE0,981
2
2
  flwr/client/__init__.py,sha256=2T4enmlE4PsoKiGTvXwBKSlhOjZ7MXRy5oCGNf0UH9Y,1111
3
- flwr/client/app.py,sha256=AkJXshBH0zKLqkm3dQQ4KYFrwNmLdjzLNZvgzfKiCOc,19243
4
- flwr/client/client.py,sha256=LmGguS2YV_BmTG-P3NXvxeljbuJ2d1OkFYRY5pQz-bQ,8198
3
+ flwr/client/app.py,sha256=B0pa8BZ5oLYRs1Fs7KhqYOxM3Sy6w9LmUt-hQfjTlBA,19242
4
+ flwr/client/client.py,sha256=ATcsqAMS9zpMBJ9ZUbBeB7BEPWX_VWISONy0p6Wxl5g,8210
5
5
  flwr/client/dpfedavg_numpy_client.py,sha256=0XryFdCMM_RLiNLCr6evLp-6R7ZjeMmRUROIgzRmtmc,7215
6
- flwr/client/flower.py,sha256=PPYROFVnu7NEe7sR8XFVbkfvfwecIuQc5-VllPz0LlQ,4049
6
+ flwr/client/flower.py,sha256=Xh3v7NgAliaLEjwMC6hhq_E0YxaVbHqwEd0HqwWe2lo,4059
7
7
  flwr/client/grpc_client/__init__.py,sha256=LsnbqXiJhgQcB0XzAlUQgPx011Uf7Y7yabIC1HxivJ8,735
8
8
  flwr/client/grpc_client/connection.py,sha256=WJazRjWZuSLnE4jsGtJ86g9THewbAfspQ-XtmfGR8uQ,5115
9
9
  flwr/client/grpc_rere_client/__init__.py,sha256=avn6W_vHEM_yZEB1S7hCZgnTbXb6ZujqRP_vAzyXu-0,752
10
10
  flwr/client/grpc_rere_client/connection.py,sha256=NtYxwJCucI9RYieU9rVMi5wzrnvLNxhSWYmi5IiWAtc,6660
11
11
  flwr/client/message_handler/__init__.py,sha256=abHvBRJJiiaAMNgeILQbMOa6h8WqMK2BcnvxwQZFpic,719
12
- flwr/client/message_handler/message_handler.py,sha256=zf6n2itYm7pjapTzEy3tvda3e1YTotIXNxRGX_hlLOg,8393
12
+ flwr/client/message_handler/message_handler.py,sha256=vhVhFdohM3q6xz5bqcDLSTyR9viFUAkbBKHCTiB4qyQ,8414
13
13
  flwr/client/message_handler/task_handler.py,sha256=36g-gP7oH_VDpsjW2P9LH4uzlDkygY8S9FyGxjbSw34,5823
14
14
  flwr/client/middleware/__init__.py,sha256=Eo3JvAV5XqmyRySNqeiw93YNETmmP5ixEOMeBA6ah4w,769
15
15
  flwr/client/middleware/utils.py,sha256=QUghso_SWsKTUPfKwrtBwPyyJoEI9AV9hRY2acu1TYE,1168
16
- flwr/client/node_state.py,sha256=3NUHqNokmFAN7Rv0rWIWnoaBKyYH9K3Ir0exGC_nx4M,1749
17
- flwr/client/node_state_tests.py,sha256=SX1kiXQmDP11eJTkaj1BFoKQpP_6XFaMQukT3_sOT6c,1939
18
- flwr/client/numpy_client.py,sha256=c9OMP2c61dr_oNy88osWwQjsOyimzJXeldlN34UAo2c,10276
16
+ flwr/client/node_state.py,sha256=0hk1RZuFZ3_S7Y8Q0BCP60NljxnT9vqPmIflvCCGxnQ,1819
17
+ flwr/client/node_state_tests.py,sha256=H3sO526boAlDS491fO5xvZhl5XNDzzl-I8DiaVmnsXM,2195
18
+ flwr/client/numpy_client.py,sha256=42e8_gZU5gwzpvVXQr6LEWEGfpWYTcQ0MY0F0owNlAc,10310
19
19
  flwr/client/rest_client/__init__.py,sha256=ThwOnkMdzxo_UuyTI47Q7y9oSpuTgNT2OuFvJCfuDiw,735
20
20
  flwr/client/rest_client/connection.py,sha256=j8BpGRM4mGJrkw7Yncpwk-BJ6KOw0C3PS3tqLfsUUes,11762
21
- flwr/client/run_state.py,sha256=d4UmHLt9zMd0sTgnVdRsLgr1QEx0l5PtJ0dPv2NSVVw,867
22
21
  flwr/client/secure_aggregation/__init__.py,sha256=XCDycteBTivym0zwkwqXhFMCAoDoHBZQg5GxdMnFCfA,888
23
22
  flwr/client/secure_aggregation/handler.py,sha256=oRyGCerz92aED7UydYwJ7OFVxUwHqedG3PshHrdZq-Y,1522
24
23
  flwr/client/secure_aggregation/secaggplus_handler.py,sha256=2jKtRhoJaVRmMJgJ2v8VRUCw2ko7uhTL2_h8CZVFwZA,18928
25
- flwr/client/typing.py,sha256=75vC6dpzv99PLRztPzaGPik2OifFZbgxvHVDMIA_C30,1219
24
+ flwr/client/typing.py,sha256=F6E2sDRrUirWdGQlfBR4YOaAJ-TYn7XiLuQMG6bMXrI,1218
26
25
  flwr/common/__init__.py,sha256=qttep0POwoigzB5pcraZa4YMt9jsCSfeibcrTQMUjIc,2884
27
26
  flwr/common/address.py,sha256=iTAN9jtmIGMrWFnx9XZQl45ZEtQJVZZLYPRBSNVARGI,1882
28
27
  flwr/common/configsrecord.py,sha256=i41syiXeG4CyWr4ujOCxTDBZKiClNnNgR4xbufxThh0,4799
29
28
  flwr/common/constant.py,sha256=J-BpHv6OFmrA37SeS4En8Nsy7yJdoNJWHCtbdnjIxSc,1120
29
+ flwr/common/context.py,sha256=OP4mTxsCdU2LS63TegdLHaEDpxULZ4AQ4ybZl7jEdDU,1329
30
30
  flwr/common/date.py,sha256=UWhBZj49yX9LD4BmatS_ZFZu_-kweGh0KQJ1djyWWH4,891
31
31
  flwr/common/dp.py,sha256=hF45cPElXxcQsh4AoquAyaTrNi0xCrIcKx7xOcV_1XU,1782
32
- flwr/common/flowercontext.py,sha256=pC8Vjw9ToNX4i8oMuvSSJrZEccJhPN9i60b1apQnQWE,2562
33
32
  flwr/common/grpc.py,sha256=qVLB0d6bCuaBRW5YB0vEZXsR7Bo3R2lh4ONiCocqwRI,2270
34
33
  flwr/common/logger.py,sha256=qX_gqEyrmGOH0x_r8uQ1Vskz4fGvEij9asdo4DUOPY8,4135
34
+ flwr/common/message.py,sha256=GQuhFmvJIrCG6h4L-jQMsn6KYh-nmNqNNo5qVe7ye5w,1847
35
35
  flwr/common/metricsrecord.py,sha256=n9789VVdjEPX0o7Je_1wRuzs7LkLh5d37QRyN0iVmRY,4783
36
36
  flwr/common/parameter.py,sha256=-bFAUayToYDF50FZGrBC1hQYJCQDtB2bbr3ZuVLMtdE,2095
37
37
  flwr/common/parametersrecord.py,sha256=v5RmvJI1Y5iJhgIhdatZj7FItRRuaEmTKkCGDRFPQDU,4413
38
38
  flwr/common/recordset.py,sha256=OeRcBMGqx9vutWRz1xkujBPHlVpU58R1EcFRHEQrePo,2351
39
- flwr/common/recordset_compat.py,sha256=6j9NWzb_DJ4RApPnsHtKQnB2jKs-ToUjkSVeTxt3XBI,13611
39
+ flwr/common/recordset_compat.py,sha256=4tbpHIPZM9V-DuVFuTdLbdb8N7-rMT0nT0uj22gaS7g,13730
40
40
  flwr/common/retry_invoker.py,sha256=H_hKqKaEI8vZPywWmoAtJYkcUnKhlYc4kV63zRY0kWA,10856
41
41
  flwr/common/secure_aggregation/__init__.py,sha256=29nHIUO2L8-KhNHQ2KmIgRo_4CPkq4LgLCUN0on5FgI,731
42
42
  flwr/common/secure_aggregation/crypto/__init__.py,sha256=dz7pVx2aPrHxr_AwgO5mIiTzu4PcvUxRq9NLBbFcsf8,738
@@ -46,7 +46,7 @@ flwr/common/secure_aggregation/ndarrays_arithmetic.py,sha256=KAHCEHGSTJ6mCgnC8dT
46
46
  flwr/common/secure_aggregation/quantization.py,sha256=appui7GGrkRPsupF59TkapeV4Na_CyPi73JtJ1pimdI,2310
47
47
  flwr/common/secure_aggregation/secaggplus_constants.py,sha256=m5UDo7IgRkMS3yixhzz7DhhAv6VAQMCghglMygSPU_k,1606
48
48
  flwr/common/secure_aggregation/secaggplus_utils.py,sha256=PleDyDu7jHNAfbRoEaoQiOjxG6iMl9yA8rNKYTfnyFw,3155
49
- flwr/common/serde.py,sha256=WGTBE1uj0iUYLMWFAV-798qJq8ThA9TZf6WvNLQAHXc,24844
49
+ flwr/common/serde.py,sha256=mI4K91XJ2lnxbdLSK93djkdrQmc_iZENvH_yKKSHo-E,26794
50
50
  flwr/common/telemetry.py,sha256=se_-pHgEWcmN09ChSpTeek72l1UJHf7GbwXBB1KXBjQ,7683
51
51
  flwr/common/typing.py,sha256=3Wu6Ol1Ja6Gb0WdlcXVEn1EHYJbc4oRRJA81vEegxBo,4382
52
52
  flwr/common/version.py,sha256=A0MKvyKPrV8wLg0YCAODTqM71v26NEH36c6JYtfgg0o,667
@@ -138,11 +138,11 @@ flwr/server/utils/validator.py,sha256=b_3ahGkSPn4M3TPYaNiY5DyJmnkQHJsyarccPgrhFo
138
138
  flwr/simulation/__init__.py,sha256=E2eD5FlTmZZ80u21FmWCkacrM7O4mrEHD8iXqeCaBUQ,1278
139
139
  flwr/simulation/app.py,sha256=pbkldpm3Uc9_0M2R5-8Ako26g9WxNhZW4fLJY-4YtJY,13879
140
140
  flwr/simulation/ray_transport/__init__.py,sha256=FsaAnzC4cw4DqoouBCix6496k29jACkfeIam55BvW9g,734
141
- flwr/simulation/ray_transport/ray_actor.py,sha256=S_E-7Bk0ONWx12b0ObP3CtzJSEL3yPxpFVcYfkDx6Es,17044
142
- flwr/simulation/ray_transport/ray_client_proxy.py,sha256=RziUVY9PmuI8fJEbra-Vk9oWwRxALDZOeF1fAW-a9wg,9430
141
+ flwr/simulation/ray_transport/ray_actor.py,sha256=G_g50ISt3Knf0zuX1wmw39gsDXSoMI5f3rmYZWGrUh4,17062
142
+ flwr/simulation/ray_transport/ray_client_proxy.py,sha256=UxQEzWmklp3WO2V7LH5vNyAgYL7KYFFZQa1HTUSgEqY,9429
143
143
  flwr/simulation/ray_transport/utils.py,sha256=e0mkFOgOXSJHSQdiipoggF-DLBXaJZVytx9auQ35fCg,3368
144
- flwr_nightly-1.7.0.dev20240124.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
145
- flwr_nightly-1.7.0.dev20240124.dist-info/METADATA,sha256=-jpfLXNP3X7t1PWjDCwR4Fl-5TKxBkNYSpH6iyLptHU,13440
146
- flwr_nightly-1.7.0.dev20240124.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
147
- flwr_nightly-1.7.0.dev20240124.dist-info/entry_points.txt,sha256=1uLlD5tIunkzALMfMWnqjdE_D5hRUX_I1iMmOMv6tZI,181
148
- flwr_nightly-1.7.0.dev20240124.dist-info/RECORD,,
144
+ flwr_nightly-1.7.0.dev20240126.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
145
+ flwr_nightly-1.7.0.dev20240126.dist-info/METADATA,sha256=kigwmhoqGNgCqt0T-xOjxzJDhDvu4ycW1vttbQpB5lk,13578
146
+ flwr_nightly-1.7.0.dev20240126.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
147
+ flwr_nightly-1.7.0.dev20240126.dist-info/entry_points.txt,sha256=1uLlD5tIunkzALMfMWnqjdE_D5hRUX_I1iMmOMv6tZI,181
148
+ flwr_nightly-1.7.0.dev20240126.dist-info/RECORD,,