flwr-nightly 1.13.0.dev20241022__py3-none-any.whl → 1.13.0.dev20241023__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.
Potentially problematic release.
This version of flwr-nightly might be problematic. Click here for more details.
- flwr/client/app.py +2 -3
- flwr/client/supernode/app.py +6 -8
- flwr/common/constant.py +29 -0
- flwr/common/typing.py +9 -0
- flwr/server/app.py +70 -1
- flwr/server/serverapp/app.py +59 -1
- flwr/server/superlink/linkstate/in_memory_linkstate.py +101 -11
- flwr/server/superlink/linkstate/linkstate.py +49 -1
- flwr/server/superlink/linkstate/sqlite_linkstate.py +122 -21
- flwr/server/superlink/linkstate/utils.py +57 -1
- flwr/simulation/run_simulation.py +12 -4
- {flwr_nightly-1.13.0.dev20241022.dist-info → flwr_nightly-1.13.0.dev20241023.dist-info}/METADATA +1 -1
- {flwr_nightly-1.13.0.dev20241022.dist-info → flwr_nightly-1.13.0.dev20241023.dist-info}/RECORD +16 -16
- {flwr_nightly-1.13.0.dev20241022.dist-info → flwr_nightly-1.13.0.dev20241023.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.13.0.dev20241022.dist-info → flwr_nightly-1.13.0.dev20241023.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.13.0.dev20241022.dist-info → flwr_nightly-1.13.0.dev20241023.dist-info}/entry_points.txt +0 -0
flwr/client/app.py
CHANGED
|
@@ -37,6 +37,8 @@ from flwr.common import GRPC_MAX_MESSAGE_LENGTH, Context, EventType, Message, ev
|
|
|
37
37
|
from flwr.common.address import parse_address
|
|
38
38
|
from flwr.common.constant import (
|
|
39
39
|
CLIENTAPPIO_API_DEFAULT_ADDRESS,
|
|
40
|
+
ISOLATION_MODE_PROCESS,
|
|
41
|
+
ISOLATION_MODE_SUBPROCESS,
|
|
40
42
|
MISSING_EXTRA_REST,
|
|
41
43
|
RUN_ID_NUM_BYTES,
|
|
42
44
|
TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
@@ -62,9 +64,6 @@ from .message_handler.message_handler import handle_control_message
|
|
|
62
64
|
from .numpy_client import NumPyClient
|
|
63
65
|
from .run_info_store import DeprecatedRunInfoStore
|
|
64
66
|
|
|
65
|
-
ISOLATION_MODE_SUBPROCESS = "subprocess"
|
|
66
|
-
ISOLATION_MODE_PROCESS = "process"
|
|
67
|
-
|
|
68
67
|
|
|
69
68
|
def _check_actionable_client(
|
|
70
69
|
client: Optional[Client], client_fn: Optional[ClientFnExt]
|
flwr/client/supernode/app.py
CHANGED
|
@@ -31,6 +31,8 @@ from flwr.common import EventType, event
|
|
|
31
31
|
from flwr.common.config import parse_config_args
|
|
32
32
|
from flwr.common.constant import (
|
|
33
33
|
FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
|
|
34
|
+
ISOLATION_MODE_PROCESS,
|
|
35
|
+
ISOLATION_MODE_SUBPROCESS,
|
|
34
36
|
TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
35
37
|
TRANSPORT_TYPE_GRPC_RERE,
|
|
36
38
|
TRANSPORT_TYPE_REST,
|
|
@@ -38,11 +40,7 @@ from flwr.common.constant import (
|
|
|
38
40
|
from flwr.common.exit_handlers import register_exit_handlers
|
|
39
41
|
from flwr.common.logger import log, warn_deprecated_feature
|
|
40
42
|
|
|
41
|
-
from ..app import
|
|
42
|
-
ISOLATION_MODE_PROCESS,
|
|
43
|
-
ISOLATION_MODE_SUBPROCESS,
|
|
44
|
-
start_client_internal,
|
|
45
|
-
)
|
|
43
|
+
from ..app import start_client_internal
|
|
46
44
|
from ..clientapp.utils import get_load_client_app_fn
|
|
47
45
|
|
|
48
46
|
|
|
@@ -200,10 +198,10 @@ def _parse_args_run_supernode() -> argparse.ArgumentParser:
|
|
|
200
198
|
ISOLATION_MODE_SUBPROCESS,
|
|
201
199
|
ISOLATION_MODE_PROCESS,
|
|
202
200
|
],
|
|
203
|
-
help="Isolation mode when running `ClientApp` (optional, possible values: "
|
|
204
|
-
"`subprocess`, `process`). By default, `ClientApp` runs in the same process "
|
|
201
|
+
help="Isolation mode when running a `ClientApp` (optional, possible values: "
|
|
202
|
+
"`subprocess`, `process`). By default, a `ClientApp` runs in the same process "
|
|
205
203
|
"that executes the SuperNode. Use `subprocess` to configure SuperNode to run "
|
|
206
|
-
"`ClientApp` in a subprocess. Use `process` to indicate that a separate "
|
|
204
|
+
"a `ClientApp` in a subprocess. Use `process` to indicate that a separate "
|
|
207
205
|
"independent process gets created outside of SuperNode.",
|
|
208
206
|
)
|
|
209
207
|
parser.add_argument(
|
flwr/common/constant.py
CHANGED
|
@@ -83,6 +83,10 @@ GRPC_ADAPTER_METADATA_MESSAGE_QUALNAME_KEY = "grpc-message-qualname"
|
|
|
83
83
|
# Message TTL
|
|
84
84
|
MESSAGE_TTL_TOLERANCE = 1e-1
|
|
85
85
|
|
|
86
|
+
# Isolation modes
|
|
87
|
+
ISOLATION_MODE_SUBPROCESS = "subprocess"
|
|
88
|
+
ISOLATION_MODE_PROCESS = "process"
|
|
89
|
+
|
|
86
90
|
|
|
87
91
|
class MessageType:
|
|
88
92
|
"""Message type."""
|
|
@@ -128,3 +132,28 @@ class ErrorCode:
|
|
|
128
132
|
def __new__(cls) -> ErrorCode:
|
|
129
133
|
"""Prevent instantiation."""
|
|
130
134
|
raise TypeError(f"{cls.__name__} cannot be instantiated.")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class Status:
|
|
138
|
+
"""Run status."""
|
|
139
|
+
|
|
140
|
+
PENDING = "pending"
|
|
141
|
+
STARTING = "starting"
|
|
142
|
+
RUNNING = "running"
|
|
143
|
+
FINISHED = "finished"
|
|
144
|
+
|
|
145
|
+
def __new__(cls) -> Status:
|
|
146
|
+
"""Prevent instantiation."""
|
|
147
|
+
raise TypeError(f"{cls.__name__} cannot be instantiated.")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class SubStatus:
|
|
151
|
+
"""Run sub-status."""
|
|
152
|
+
|
|
153
|
+
COMPLETED = "completed"
|
|
154
|
+
FAILED = "failed"
|
|
155
|
+
STOPPED = "stopped"
|
|
156
|
+
|
|
157
|
+
def __new__(cls) -> SubStatus:
|
|
158
|
+
"""Prevent instantiation."""
|
|
159
|
+
raise TypeError(f"{cls.__name__} cannot be instantiated.")
|
flwr/common/typing.py
CHANGED
|
@@ -218,6 +218,15 @@ class Run:
|
|
|
218
218
|
override_config: UserConfig
|
|
219
219
|
|
|
220
220
|
|
|
221
|
+
@dataclass
|
|
222
|
+
class RunStatus:
|
|
223
|
+
"""Run status information."""
|
|
224
|
+
|
|
225
|
+
status: str
|
|
226
|
+
sub_status: str
|
|
227
|
+
details: str
|
|
228
|
+
|
|
229
|
+
|
|
221
230
|
@dataclass
|
|
222
231
|
class Fab:
|
|
223
232
|
"""Fab file representation."""
|
flwr/server/app.py
CHANGED
|
@@ -17,12 +17,14 @@
|
|
|
17
17
|
import argparse
|
|
18
18
|
import csv
|
|
19
19
|
import importlib.util
|
|
20
|
+
import subprocess
|
|
20
21
|
import sys
|
|
21
22
|
import threading
|
|
22
23
|
from collections.abc import Sequence
|
|
23
|
-
from logging import INFO, WARN
|
|
24
|
+
from logging import DEBUG, INFO, WARN
|
|
24
25
|
from os.path import isfile
|
|
25
26
|
from pathlib import Path
|
|
27
|
+
from time import sleep
|
|
26
28
|
from typing import Optional
|
|
27
29
|
|
|
28
30
|
import grpc
|
|
@@ -42,10 +44,13 @@ from flwr.common.constant import (
|
|
|
42
44
|
FLEET_API_GRPC_BIDI_DEFAULT_ADDRESS,
|
|
43
45
|
FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
|
|
44
46
|
FLEET_API_REST_DEFAULT_ADDRESS,
|
|
47
|
+
ISOLATION_MODE_PROCESS,
|
|
48
|
+
ISOLATION_MODE_SUBPROCESS,
|
|
45
49
|
MISSING_EXTRA_REST,
|
|
46
50
|
TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
47
51
|
TRANSPORT_TYPE_GRPC_RERE,
|
|
48
52
|
TRANSPORT_TYPE_REST,
|
|
53
|
+
Status,
|
|
49
54
|
)
|
|
50
55
|
from flwr.common.exit_handlers import register_exit_handlers
|
|
51
56
|
from flwr.common.logger import log
|
|
@@ -53,6 +58,7 @@ from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
|
|
|
53
58
|
private_key_to_bytes,
|
|
54
59
|
public_key_to_bytes,
|
|
55
60
|
)
|
|
61
|
+
from flwr.common.typing import RunStatus
|
|
56
62
|
from flwr.proto.fleet_pb2_grpc import ( # pylint: disable=E0611
|
|
57
63
|
add_FleetServicer_to_server,
|
|
58
64
|
)
|
|
@@ -333,6 +339,15 @@ def run_superlink() -> None:
|
|
|
333
339
|
)
|
|
334
340
|
grpc_servers.append(exec_server)
|
|
335
341
|
|
|
342
|
+
if args.isolation == ISOLATION_MODE_SUBPROCESS:
|
|
343
|
+
# Scheduler thread
|
|
344
|
+
scheduler_th = threading.Thread(
|
|
345
|
+
target=_flwr_serverapp_scheduler,
|
|
346
|
+
args=(state_factory, args.driver_api_address),
|
|
347
|
+
)
|
|
348
|
+
scheduler_th.start()
|
|
349
|
+
bckg_threads.append(scheduler_th)
|
|
350
|
+
|
|
336
351
|
# Graceful shutdown
|
|
337
352
|
register_exit_handlers(
|
|
338
353
|
event_type=EventType.RUN_SUPERLINK_LEAVE,
|
|
@@ -349,6 +364,47 @@ def run_superlink() -> None:
|
|
|
349
364
|
driver_server.wait_for_termination(timeout=1)
|
|
350
365
|
|
|
351
366
|
|
|
367
|
+
def _flwr_serverapp_scheduler(
|
|
368
|
+
state_factory: LinkStateFactory, driver_api_address: str
|
|
369
|
+
) -> None:
|
|
370
|
+
log(DEBUG, "Started flwr-serverapp scheduler thread.")
|
|
371
|
+
|
|
372
|
+
state = state_factory.state()
|
|
373
|
+
|
|
374
|
+
# Periodically check for a pending run in the LinkState
|
|
375
|
+
while True:
|
|
376
|
+
sleep(3)
|
|
377
|
+
pending_run_id = state.get_pending_run_id()
|
|
378
|
+
|
|
379
|
+
if pending_run_id:
|
|
380
|
+
|
|
381
|
+
# Set run as starting
|
|
382
|
+
state.update_run_status(
|
|
383
|
+
run_id=pending_run_id, new_status=RunStatus(Status.STARTING, "", "")
|
|
384
|
+
)
|
|
385
|
+
log(
|
|
386
|
+
INFO,
|
|
387
|
+
"Launching `flwr-serverapp` subprocess with run-id %d. "
|
|
388
|
+
"Connects to SuperLink on %s",
|
|
389
|
+
pending_run_id,
|
|
390
|
+
driver_api_address,
|
|
391
|
+
)
|
|
392
|
+
# Start ServerApp subprocess
|
|
393
|
+
command = [
|
|
394
|
+
"flwr-serverapp",
|
|
395
|
+
"--superlink",
|
|
396
|
+
driver_api_address,
|
|
397
|
+
"--run-id",
|
|
398
|
+
str(pending_run_id),
|
|
399
|
+
]
|
|
400
|
+
subprocess.run(
|
|
401
|
+
command,
|
|
402
|
+
stdout=None,
|
|
403
|
+
stderr=None,
|
|
404
|
+
check=True,
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
|
|
352
408
|
def _format_address(address: str) -> tuple[str, str, int]:
|
|
353
409
|
parsed_address = parse_address(address)
|
|
354
410
|
if not parsed_address:
|
|
@@ -634,6 +690,19 @@ def _add_args_common(parser: argparse.ArgumentParser) -> None:
|
|
|
634
690
|
"to create a secure connection.",
|
|
635
691
|
type=str,
|
|
636
692
|
)
|
|
693
|
+
parser.add_argument(
|
|
694
|
+
"--isolation",
|
|
695
|
+
default=ISOLATION_MODE_SUBPROCESS,
|
|
696
|
+
required=False,
|
|
697
|
+
choices=[
|
|
698
|
+
ISOLATION_MODE_SUBPROCESS,
|
|
699
|
+
ISOLATION_MODE_PROCESS,
|
|
700
|
+
],
|
|
701
|
+
help="Isolation mode when running a `ServerApp` (`subprocess` by default, "
|
|
702
|
+
"possible values: `subprocess`, `process`). Use `subprocess` to configure "
|
|
703
|
+
"SuperLink to run a `ServerApp` in a subprocess. Use `process` to indicate "
|
|
704
|
+
"that a separate independent process gets created outside of SuperLink.",
|
|
705
|
+
)
|
|
637
706
|
parser.add_argument(
|
|
638
707
|
"--database",
|
|
639
708
|
help="A string representing the path to the database "
|
flwr/server/serverapp/app.py
CHANGED
|
@@ -14,7 +14,65 @@
|
|
|
14
14
|
# ==============================================================================
|
|
15
15
|
"""Flower ServerApp process."""
|
|
16
16
|
|
|
17
|
+
import argparse
|
|
18
|
+
from logging import DEBUG, INFO
|
|
19
|
+
from typing import Optional
|
|
20
|
+
|
|
21
|
+
from flwr.common.logger import log
|
|
22
|
+
from flwr.server.driver.grpc_driver import GrpcDriver
|
|
23
|
+
|
|
17
24
|
|
|
18
25
|
def flwr_serverapp() -> None:
|
|
19
26
|
"""Run process-isolated Flower ServerApp."""
|
|
20
|
-
|
|
27
|
+
log(INFO, "Starting Flower ServerApp")
|
|
28
|
+
|
|
29
|
+
parser = argparse.ArgumentParser(
|
|
30
|
+
description="Run a Flower ServerApp",
|
|
31
|
+
)
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"--superlink",
|
|
34
|
+
type=str,
|
|
35
|
+
help="Address of SuperLink's DriverAPI",
|
|
36
|
+
)
|
|
37
|
+
parser.add_argument(
|
|
38
|
+
"--run-id",
|
|
39
|
+
type=int,
|
|
40
|
+
required=False,
|
|
41
|
+
help="Id of the Run this process should start. If not supplied, this "
|
|
42
|
+
"function will request a pending run to the LinkState.",
|
|
43
|
+
)
|
|
44
|
+
args = parser.parse_args()
|
|
45
|
+
|
|
46
|
+
log(
|
|
47
|
+
DEBUG,
|
|
48
|
+
"Staring isolated `ServerApp` connected to SuperLink DriverAPI at %s "
|
|
49
|
+
"for run-id %s",
|
|
50
|
+
args.superlink,
|
|
51
|
+
args.run_id,
|
|
52
|
+
)
|
|
53
|
+
run_serverapp(superlink=args.superlink, run_id=args.run_id)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def run_serverapp( # pylint: disable=R0914
|
|
57
|
+
superlink: str,
|
|
58
|
+
run_id: Optional[int] = None,
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Run Flower ServerApp process.
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
superlink : str
|
|
65
|
+
Address of SuperLink
|
|
66
|
+
run_id : Optional[int] (default: None)
|
|
67
|
+
Unique identifier of a Run registered at the LinkState. If not supplied,
|
|
68
|
+
this function will request a pending run to the LinkState.
|
|
69
|
+
"""
|
|
70
|
+
_ = GrpcDriver(
|
|
71
|
+
run_id=run_id if run_id else 0,
|
|
72
|
+
driver_service_address=superlink,
|
|
73
|
+
root_certificates=None,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Then, GetServerInputs
|
|
77
|
+
|
|
78
|
+
# Then, run ServerApp
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import threading
|
|
19
19
|
import time
|
|
20
|
+
from dataclasses import dataclass
|
|
20
21
|
from logging import ERROR, WARNING
|
|
21
22
|
from typing import Optional
|
|
22
23
|
from uuid import UUID, uuid4
|
|
@@ -26,13 +27,31 @@ from flwr.common.constant import (
|
|
|
26
27
|
MESSAGE_TTL_TOLERANCE,
|
|
27
28
|
NODE_ID_NUM_BYTES,
|
|
28
29
|
RUN_ID_NUM_BYTES,
|
|
30
|
+
Status,
|
|
29
31
|
)
|
|
30
|
-
from flwr.common.typing import Run, UserConfig
|
|
32
|
+
from flwr.common.typing import Run, RunStatus, UserConfig
|
|
31
33
|
from flwr.proto.task_pb2 import TaskIns, TaskRes # pylint: disable=E0611
|
|
32
34
|
from flwr.server.superlink.linkstate.linkstate import LinkState
|
|
33
35
|
from flwr.server.utils import validate_task_ins_or_res
|
|
34
36
|
|
|
35
|
-
from .utils import
|
|
37
|
+
from .utils import (
|
|
38
|
+
generate_rand_int_from_bytes,
|
|
39
|
+
has_valid_sub_status,
|
|
40
|
+
is_valid_transition,
|
|
41
|
+
make_node_unavailable_taskres,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class RunRecord:
|
|
47
|
+
"""The record of a specific run, including its status and timestamps."""
|
|
48
|
+
|
|
49
|
+
run: Run
|
|
50
|
+
status: RunStatus
|
|
51
|
+
pending_at: str = ""
|
|
52
|
+
starting_at: str = ""
|
|
53
|
+
running_at: str = ""
|
|
54
|
+
finished_at: str = ""
|
|
36
55
|
|
|
37
56
|
|
|
38
57
|
class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
|
|
@@ -44,8 +63,8 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
|
|
|
44
63
|
self.node_ids: dict[int, tuple[float, float]] = {}
|
|
45
64
|
self.public_key_to_node_id: dict[bytes, int] = {}
|
|
46
65
|
|
|
47
|
-
# Map run_id to
|
|
48
|
-
self.run_ids: dict[int,
|
|
66
|
+
# Map run_id to RunRecord
|
|
67
|
+
self.run_ids: dict[int, RunRecord] = {}
|
|
49
68
|
self.task_ins_store: dict[UUID, TaskIns] = {}
|
|
50
69
|
self.task_res_store: dict[UUID, TaskRes] = {}
|
|
51
70
|
|
|
@@ -351,13 +370,22 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
|
|
|
351
370
|
run_id = generate_rand_int_from_bytes(RUN_ID_NUM_BYTES)
|
|
352
371
|
|
|
353
372
|
if run_id not in self.run_ids:
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
373
|
+
run_record = RunRecord(
|
|
374
|
+
run=Run(
|
|
375
|
+
run_id=run_id,
|
|
376
|
+
fab_id=fab_id if fab_id else "",
|
|
377
|
+
fab_version=fab_version if fab_version else "",
|
|
378
|
+
fab_hash=fab_hash if fab_hash else "",
|
|
379
|
+
override_config=override_config,
|
|
380
|
+
),
|
|
381
|
+
status=RunStatus(
|
|
382
|
+
status=Status.PENDING,
|
|
383
|
+
sub_status="",
|
|
384
|
+
details="",
|
|
385
|
+
),
|
|
386
|
+
pending_at=now().isoformat(),
|
|
360
387
|
)
|
|
388
|
+
self.run_ids[run_id] = run_record
|
|
361
389
|
return run_id
|
|
362
390
|
log(ERROR, "Unexpected run creation failure.")
|
|
363
391
|
return 0
|
|
@@ -401,7 +429,69 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
|
|
|
401
429
|
if run_id not in self.run_ids:
|
|
402
430
|
log(ERROR, "`run_id` is invalid")
|
|
403
431
|
return None
|
|
404
|
-
return self.run_ids[run_id]
|
|
432
|
+
return self.run_ids[run_id].run
|
|
433
|
+
|
|
434
|
+
def get_run_status(self, run_ids: set[int]) -> dict[int, RunStatus]:
|
|
435
|
+
"""Retrieve the statuses for the specified runs."""
|
|
436
|
+
with self.lock:
|
|
437
|
+
return {
|
|
438
|
+
run_id: self.run_ids[run_id].status
|
|
439
|
+
for run_id in set(run_ids)
|
|
440
|
+
if run_id in self.run_ids
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
def update_run_status(self, run_id: int, new_status: RunStatus) -> bool:
|
|
444
|
+
"""Update the status of the run with the specified `run_id`."""
|
|
445
|
+
with self.lock:
|
|
446
|
+
# Check if the run_id exists
|
|
447
|
+
if run_id not in self.run_ids:
|
|
448
|
+
log(ERROR, "`run_id` is invalid")
|
|
449
|
+
return False
|
|
450
|
+
|
|
451
|
+
# Check if the status transition is valid
|
|
452
|
+
current_status = self.run_ids[run_id].status
|
|
453
|
+
if not is_valid_transition(current_status, new_status):
|
|
454
|
+
log(
|
|
455
|
+
ERROR,
|
|
456
|
+
'Invalid status transition: from "%s" to "%s"',
|
|
457
|
+
current_status.status,
|
|
458
|
+
new_status.status,
|
|
459
|
+
)
|
|
460
|
+
return False
|
|
461
|
+
|
|
462
|
+
# Check if the sub-status is valid
|
|
463
|
+
if not has_valid_sub_status(current_status):
|
|
464
|
+
log(
|
|
465
|
+
ERROR,
|
|
466
|
+
'Invalid sub-status "%s" for status "%s"',
|
|
467
|
+
current_status.sub_status,
|
|
468
|
+
current_status.status,
|
|
469
|
+
)
|
|
470
|
+
return False
|
|
471
|
+
|
|
472
|
+
# Update the status
|
|
473
|
+
run_record = self.run_ids[run_id]
|
|
474
|
+
if new_status.status == Status.STARTING:
|
|
475
|
+
run_record.starting_at = now().isoformat()
|
|
476
|
+
elif new_status.status == Status.RUNNING:
|
|
477
|
+
run_record.running_at = now().isoformat()
|
|
478
|
+
elif new_status.status == Status.FINISHED:
|
|
479
|
+
run_record.finished_at = now().isoformat()
|
|
480
|
+
run_record.status = new_status
|
|
481
|
+
return True
|
|
482
|
+
|
|
483
|
+
def get_pending_run_id(self) -> Optional[int]:
|
|
484
|
+
"""Get the `run_id` of a run with `Status.PENDING` status, if any."""
|
|
485
|
+
pending_run_id = None
|
|
486
|
+
|
|
487
|
+
# Loop through all registered runs
|
|
488
|
+
for run_id, run_rec in self.run_ids.items():
|
|
489
|
+
# Break once a pending run is found
|
|
490
|
+
if run_rec.status.status == Status.PENDING:
|
|
491
|
+
pending_run_id = run_id
|
|
492
|
+
break
|
|
493
|
+
|
|
494
|
+
return pending_run_id
|
|
405
495
|
|
|
406
496
|
def acknowledge_ping(self, node_id: int, ping_interval: float) -> bool:
|
|
407
497
|
"""Acknowledge a ping received from a node, serving as a heartbeat."""
|
|
@@ -19,7 +19,7 @@ import abc
|
|
|
19
19
|
from typing import Optional
|
|
20
20
|
from uuid import UUID
|
|
21
21
|
|
|
22
|
-
from flwr.common.typing import Run, UserConfig
|
|
22
|
+
from flwr.common.typing import Run, RunStatus, UserConfig
|
|
23
23
|
from flwr.proto.task_pb2 import TaskIns, TaskRes # pylint: disable=E0611
|
|
24
24
|
|
|
25
25
|
|
|
@@ -178,6 +178,54 @@ class LinkState(abc.ABC): # pylint: disable=R0904
|
|
|
178
178
|
- `fab_version`: The version of the FAB used in the specified run.
|
|
179
179
|
"""
|
|
180
180
|
|
|
181
|
+
@abc.abstractmethod
|
|
182
|
+
def get_run_status(self, run_ids: set[int]) -> dict[int, RunStatus]:
|
|
183
|
+
"""Retrieve the statuses for the specified runs.
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
run_ids : set[int]
|
|
188
|
+
A set of run identifiers for which to retrieve statuses.
|
|
189
|
+
|
|
190
|
+
Returns
|
|
191
|
+
-------
|
|
192
|
+
dict[int, RunStatus]
|
|
193
|
+
A dictionary mapping each valid run ID to its corresponding status.
|
|
194
|
+
|
|
195
|
+
Notes
|
|
196
|
+
-----
|
|
197
|
+
Only valid run IDs that exist in the State will be included in the returned
|
|
198
|
+
dictionary. If a run ID is not found, it will be omitted from the result.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
@abc.abstractmethod
|
|
202
|
+
def update_run_status(self, run_id: int, new_status: RunStatus) -> bool:
|
|
203
|
+
"""Update the status of the run with the specified `run_id`.
|
|
204
|
+
|
|
205
|
+
Parameters
|
|
206
|
+
----------
|
|
207
|
+
run_id : int
|
|
208
|
+
The identifier of the run.
|
|
209
|
+
new_status : RunStatus
|
|
210
|
+
The new status to be assigned to the run.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
bool
|
|
215
|
+
True if the status update is successful; False otherwise.
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
@abc.abstractmethod
|
|
219
|
+
def get_pending_run_id(self) -> Optional[int]:
|
|
220
|
+
"""Get the `run_id` of a run with `Status.PENDING` status.
|
|
221
|
+
|
|
222
|
+
Returns
|
|
223
|
+
-------
|
|
224
|
+
Optional[int]
|
|
225
|
+
The `run_id` of a `Run` that is pending to be started; None if
|
|
226
|
+
there is no Run pending.
|
|
227
|
+
"""
|
|
228
|
+
|
|
181
229
|
@abc.abstractmethod
|
|
182
230
|
def store_server_private_public_key(
|
|
183
231
|
self, private_key: bytes, public_key: bytes
|
|
@@ -30,8 +30,9 @@ from flwr.common.constant import (
|
|
|
30
30
|
MESSAGE_TTL_TOLERANCE,
|
|
31
31
|
NODE_ID_NUM_BYTES,
|
|
32
32
|
RUN_ID_NUM_BYTES,
|
|
33
|
+
Status,
|
|
33
34
|
)
|
|
34
|
-
from flwr.common.typing import Run, UserConfig
|
|
35
|
+
from flwr.common.typing import Run, RunStatus, UserConfig
|
|
35
36
|
from flwr.proto.node_pb2 import Node # pylint: disable=E0611
|
|
36
37
|
from flwr.proto.recordset_pb2 import RecordSet # pylint: disable=E0611
|
|
37
38
|
from flwr.proto.task_pb2 import Task, TaskIns, TaskRes # pylint: disable=E0611
|
|
@@ -44,6 +45,8 @@ from .utils import (
|
|
|
44
45
|
convert_uint64_to_sint64,
|
|
45
46
|
convert_uint64_values_in_dict_to_sint64,
|
|
46
47
|
generate_rand_int_from_bytes,
|
|
48
|
+
has_valid_sub_status,
|
|
49
|
+
is_valid_transition,
|
|
47
50
|
make_node_unavailable_taskres,
|
|
48
51
|
)
|
|
49
52
|
|
|
@@ -79,7 +82,13 @@ CREATE TABLE IF NOT EXISTS run(
|
|
|
79
82
|
fab_id TEXT,
|
|
80
83
|
fab_version TEXT,
|
|
81
84
|
fab_hash TEXT,
|
|
82
|
-
override_config TEXT
|
|
85
|
+
override_config TEXT,
|
|
86
|
+
pending_at TEXT,
|
|
87
|
+
starting_at TEXT,
|
|
88
|
+
running_at TEXT,
|
|
89
|
+
finished_at TEXT,
|
|
90
|
+
sub_status TEXT,
|
|
91
|
+
details TEXT
|
|
83
92
|
);
|
|
84
93
|
"""
|
|
85
94
|
|
|
@@ -133,7 +142,7 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
|
|
|
133
142
|
self,
|
|
134
143
|
database_path: str,
|
|
135
144
|
) -> None:
|
|
136
|
-
"""Initialize an
|
|
145
|
+
"""Initialize an SqliteLinkState.
|
|
137
146
|
|
|
138
147
|
Parameters
|
|
139
148
|
----------
|
|
@@ -773,26 +782,16 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
|
|
|
773
782
|
if self.query(query, (sint64_run_id,))[0]["COUNT(*)"] == 0:
|
|
774
783
|
query = (
|
|
775
784
|
"INSERT INTO run "
|
|
776
|
-
"(run_id, fab_id, fab_version, fab_hash, override_config
|
|
777
|
-
"
|
|
785
|
+
"(run_id, fab_id, fab_version, fab_hash, override_config, pending_at, "
|
|
786
|
+
"starting_at, running_at, finished_at, sub_status, details)"
|
|
787
|
+
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"
|
|
778
788
|
)
|
|
779
789
|
if fab_hash:
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
self.query(
|
|
786
|
-
query,
|
|
787
|
-
(
|
|
788
|
-
sint64_run_id,
|
|
789
|
-
fab_id,
|
|
790
|
-
fab_version,
|
|
791
|
-
"",
|
|
792
|
-
json.dumps(override_config),
|
|
793
|
-
),
|
|
794
|
-
)
|
|
795
|
-
# Note: we need to return the uint64 value of the run_id
|
|
790
|
+
fab_id, fab_version = "", ""
|
|
791
|
+
override_config_json = json.dumps(override_config)
|
|
792
|
+
data = [sint64_run_id, fab_id, fab_version, fab_hash, override_config_json]
|
|
793
|
+
data += [now().isoformat(), "", "", "", "", ""]
|
|
794
|
+
self.query(query, tuple(data))
|
|
796
795
|
return uint64_run_id
|
|
797
796
|
log(ERROR, "Unexpected run creation failure.")
|
|
798
797
|
return 0
|
|
@@ -868,6 +867,94 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
|
|
|
868
867
|
log(ERROR, "`run_id` does not exist.")
|
|
869
868
|
return None
|
|
870
869
|
|
|
870
|
+
def get_run_status(self, run_ids: set[int]) -> dict[int, RunStatus]:
|
|
871
|
+
"""Retrieve the statuses for the specified runs."""
|
|
872
|
+
# Convert the uint64 value to sint64 for SQLite
|
|
873
|
+
sint64_run_ids = (convert_uint64_to_sint64(run_id) for run_id in set(run_ids))
|
|
874
|
+
query = f"SELECT * FROM run WHERE run_id IN ({','.join(['?'] * len(run_ids))});"
|
|
875
|
+
rows = self.query(query, tuple(sint64_run_ids))
|
|
876
|
+
|
|
877
|
+
return {
|
|
878
|
+
# Restore uint64 run IDs
|
|
879
|
+
convert_sint64_to_uint64(row["run_id"]): RunStatus(
|
|
880
|
+
status=determine_run_status(row),
|
|
881
|
+
sub_status=row["sub_status"],
|
|
882
|
+
details=row["details"],
|
|
883
|
+
)
|
|
884
|
+
for row in rows
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
def update_run_status(self, run_id: int, new_status: RunStatus) -> bool:
|
|
888
|
+
"""Update the status of the run with the specified `run_id`."""
|
|
889
|
+
# Convert the uint64 value to sint64 for SQLite
|
|
890
|
+
sint64_run_id = convert_uint64_to_sint64(run_id)
|
|
891
|
+
query = "SELECT * FROM run WHERE run_id = ?;"
|
|
892
|
+
rows = self.query(query, (sint64_run_id,))
|
|
893
|
+
|
|
894
|
+
# Check if the run_id exists
|
|
895
|
+
if not rows:
|
|
896
|
+
log(ERROR, "`run_id` is invalid")
|
|
897
|
+
return False
|
|
898
|
+
|
|
899
|
+
# Check if the status transition is valid
|
|
900
|
+
row = rows[0]
|
|
901
|
+
current_status = RunStatus(
|
|
902
|
+
status=determine_run_status(row),
|
|
903
|
+
sub_status=row["sub_status"],
|
|
904
|
+
details=row["details"],
|
|
905
|
+
)
|
|
906
|
+
if not is_valid_transition(current_status, new_status):
|
|
907
|
+
log(
|
|
908
|
+
ERROR,
|
|
909
|
+
'Invalid status transition: from "%s" to "%s"',
|
|
910
|
+
current_status.status,
|
|
911
|
+
new_status.status,
|
|
912
|
+
)
|
|
913
|
+
return False
|
|
914
|
+
|
|
915
|
+
# Check if the sub-status is valid
|
|
916
|
+
if not has_valid_sub_status(current_status):
|
|
917
|
+
log(
|
|
918
|
+
ERROR,
|
|
919
|
+
'Invalid sub-status "%s" for status "%s"',
|
|
920
|
+
current_status.sub_status,
|
|
921
|
+
current_status.status,
|
|
922
|
+
)
|
|
923
|
+
return False
|
|
924
|
+
|
|
925
|
+
# Update the status
|
|
926
|
+
query = "UPDATE run SET %s= ?, sub_status = ?, details = ? "
|
|
927
|
+
query += "WHERE run_id = ?;"
|
|
928
|
+
|
|
929
|
+
timestamp_fld = ""
|
|
930
|
+
if new_status.status == Status.STARTING:
|
|
931
|
+
timestamp_fld = "starting_at"
|
|
932
|
+
elif new_status.status == Status.RUNNING:
|
|
933
|
+
timestamp_fld = "running_at"
|
|
934
|
+
elif new_status.status == Status.FINISHED:
|
|
935
|
+
timestamp_fld = "finished_at"
|
|
936
|
+
|
|
937
|
+
data = (
|
|
938
|
+
now().isoformat(),
|
|
939
|
+
new_status.sub_status,
|
|
940
|
+
new_status.details,
|
|
941
|
+
sint64_run_id,
|
|
942
|
+
)
|
|
943
|
+
self.query(query % timestamp_fld, data)
|
|
944
|
+
return True
|
|
945
|
+
|
|
946
|
+
def get_pending_run_id(self) -> Optional[int]:
|
|
947
|
+
"""Get the `run_id` of a run with `Status.PENDING` status, if any."""
|
|
948
|
+
pending_run_id = None
|
|
949
|
+
|
|
950
|
+
# Fetch all runs with unset `starting_at` (i.e. they are in PENDING status)
|
|
951
|
+
query = "SELECT * FROM run WHERE starting_at = '' LIMIT 1;"
|
|
952
|
+
rows = self.query(query)
|
|
953
|
+
if rows:
|
|
954
|
+
pending_run_id = convert_sint64_to_uint64(rows[0]["run_id"])
|
|
955
|
+
|
|
956
|
+
return pending_run_id
|
|
957
|
+
|
|
871
958
|
def acknowledge_ping(self, node_id: int, ping_interval: float) -> bool:
|
|
872
959
|
"""Acknowledge a ping received from a node, serving as a heartbeat."""
|
|
873
960
|
sint64_node_id = convert_uint64_to_sint64(node_id)
|
|
@@ -1023,3 +1110,17 @@ def dict_to_task_res(task_dict: dict[str, Any]) -> TaskRes:
|
|
|
1023
1110
|
),
|
|
1024
1111
|
)
|
|
1025
1112
|
return result
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
def determine_run_status(row: dict[str, Any]) -> str:
|
|
1116
|
+
"""Determine the status of the run based on timestamp fields."""
|
|
1117
|
+
if row["pending_at"]:
|
|
1118
|
+
if row["starting_at"]:
|
|
1119
|
+
if row["running_at"]:
|
|
1120
|
+
if row["finished_at"]:
|
|
1121
|
+
return Status.FINISHED
|
|
1122
|
+
return Status.RUNNING
|
|
1123
|
+
return Status.STARTING
|
|
1124
|
+
return Status.PENDING
|
|
1125
|
+
run_id = convert_sint64_to_uint64(row["run_id"])
|
|
1126
|
+
raise sqlite3.IntegrityError(f"The run {run_id} does not have a valid status.")
|
|
@@ -21,7 +21,8 @@ from os import urandom
|
|
|
21
21
|
from uuid import uuid4
|
|
22
22
|
|
|
23
23
|
from flwr.common import log
|
|
24
|
-
from flwr.common.constant import ErrorCode
|
|
24
|
+
from flwr.common.constant import ErrorCode, Status, SubStatus
|
|
25
|
+
from flwr.common.typing import RunStatus
|
|
25
26
|
from flwr.proto.error_pb2 import Error # pylint: disable=E0611
|
|
26
27
|
from flwr.proto.node_pb2 import Node # pylint: disable=E0611
|
|
27
28
|
from flwr.proto.task_pb2 import Task, TaskIns, TaskRes # pylint: disable=E0611
|
|
@@ -31,6 +32,17 @@ NODE_UNAVAILABLE_ERROR_REASON = (
|
|
|
31
32
|
"It exceeds the time limit specified in its last ping."
|
|
32
33
|
)
|
|
33
34
|
|
|
35
|
+
VALID_RUN_STATUS_TRANSITIONS = {
|
|
36
|
+
(Status.PENDING, Status.STARTING),
|
|
37
|
+
(Status.STARTING, Status.RUNNING),
|
|
38
|
+
(Status.RUNNING, Status.FINISHED),
|
|
39
|
+
}
|
|
40
|
+
VALID_RUN_SUB_STATUSES = {
|
|
41
|
+
SubStatus.COMPLETED,
|
|
42
|
+
SubStatus.FAILED,
|
|
43
|
+
SubStatus.STOPPED,
|
|
44
|
+
}
|
|
45
|
+
|
|
34
46
|
|
|
35
47
|
def generate_rand_int_from_bytes(num_bytes: int) -> int:
|
|
36
48
|
"""Generate a random unsigned integer from `num_bytes` bytes."""
|
|
@@ -146,3 +158,47 @@ def make_node_unavailable_taskres(ref_taskins: TaskIns) -> TaskRes:
|
|
|
146
158
|
),
|
|
147
159
|
),
|
|
148
160
|
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def is_valid_transition(current_status: RunStatus, new_status: RunStatus) -> bool:
|
|
164
|
+
"""Check if a transition between two run statuses is valid.
|
|
165
|
+
|
|
166
|
+
Parameters
|
|
167
|
+
----------
|
|
168
|
+
current_status : RunStatus
|
|
169
|
+
The current status of the run.
|
|
170
|
+
new_status : RunStatus
|
|
171
|
+
The new status to transition to.
|
|
172
|
+
|
|
173
|
+
Returns
|
|
174
|
+
-------
|
|
175
|
+
bool
|
|
176
|
+
True if the transition is valid, False otherwise.
|
|
177
|
+
"""
|
|
178
|
+
return (
|
|
179
|
+
current_status.status,
|
|
180
|
+
new_status.status,
|
|
181
|
+
) in VALID_RUN_STATUS_TRANSITIONS
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def has_valid_sub_status(status: RunStatus) -> bool:
|
|
185
|
+
"""Check if the 'sub_status' field of the given status is valid.
|
|
186
|
+
|
|
187
|
+
Parameters
|
|
188
|
+
----------
|
|
189
|
+
status : RunStatus
|
|
190
|
+
The status object to be checked.
|
|
191
|
+
|
|
192
|
+
Returns
|
|
193
|
+
-------
|
|
194
|
+
bool
|
|
195
|
+
True if the status object has a valid sub-status, False otherwise.
|
|
196
|
+
|
|
197
|
+
Notes
|
|
198
|
+
-----
|
|
199
|
+
Only an empty string (i.e., "") is considered a valid sub-status for
|
|
200
|
+
non-finished statuses. The sub-status of a finished status cannot be empty.
|
|
201
|
+
"""
|
|
202
|
+
if status.status == Status.FINISHED:
|
|
203
|
+
return status.sub_status in VALID_RUN_SUB_STATUSES
|
|
204
|
+
return status.sub_status == ""
|
|
@@ -29,22 +29,23 @@ from typing import Any, Optional
|
|
|
29
29
|
|
|
30
30
|
from flwr.cli.config_utils import load_and_validate
|
|
31
31
|
from flwr.client import ClientApp
|
|
32
|
-
from flwr.common import EventType, event, log
|
|
32
|
+
from flwr.common import EventType, event, log, now
|
|
33
33
|
from flwr.common.config import get_fused_config_from_dir, parse_config_args
|
|
34
|
-
from flwr.common.constant import RUN_ID_NUM_BYTES
|
|
34
|
+
from flwr.common.constant import RUN_ID_NUM_BYTES, Status
|
|
35
35
|
from flwr.common.logger import (
|
|
36
36
|
set_logger_propagation,
|
|
37
37
|
update_console_handler,
|
|
38
38
|
warn_deprecated_feature,
|
|
39
39
|
warn_deprecated_feature_with_example,
|
|
40
40
|
)
|
|
41
|
-
from flwr.common.typing import Run, UserConfig
|
|
41
|
+
from flwr.common.typing import Run, RunStatus, UserConfig
|
|
42
42
|
from flwr.server.driver import Driver, InMemoryDriver
|
|
43
43
|
from flwr.server.run_serverapp import run as run_server_app
|
|
44
44
|
from flwr.server.server_app import ServerApp
|
|
45
45
|
from flwr.server.superlink.fleet import vce
|
|
46
46
|
from flwr.server.superlink.fleet.vce.backend.backend import BackendConfig
|
|
47
47
|
from flwr.server.superlink.linkstate import LinkStateFactory
|
|
48
|
+
from flwr.server.superlink.linkstate.in_memory_linkstate import RunRecord
|
|
48
49
|
from flwr.server.superlink.linkstate.utils import generate_rand_int_from_bytes
|
|
49
50
|
from flwr.simulation.ray_transport.utils import (
|
|
50
51
|
enable_tf_gpu_growth as enable_gpu_growth,
|
|
@@ -399,7 +400,14 @@ def _main_loop(
|
|
|
399
400
|
try:
|
|
400
401
|
# Register run
|
|
401
402
|
log(DEBUG, "Pre-registering run with id %s", run.run_id)
|
|
402
|
-
|
|
403
|
+
init_status = RunStatus(Status.RUNNING, "", "")
|
|
404
|
+
state_factory.state().run_ids[run.run_id] = RunRecord( # type: ignore
|
|
405
|
+
run=run,
|
|
406
|
+
status=init_status,
|
|
407
|
+
starting_at=now().isoformat(),
|
|
408
|
+
running_at=now().isoformat(),
|
|
409
|
+
finished_at="",
|
|
410
|
+
)
|
|
403
411
|
|
|
404
412
|
if server_app_run_config is None:
|
|
405
413
|
server_app_run_config = {}
|
{flwr_nightly-1.13.0.dev20241022.dist-info → flwr_nightly-1.13.0.dev20241023.dist-info}/RECORD
RENAMED
|
@@ -64,7 +64,7 @@ flwr/cli/run/__init__.py,sha256=oCd6HmQDx-sqver1gecgx-uMA38BLTSiiKpl7RGNceg,789
|
|
|
64
64
|
flwr/cli/run/run.py,sha256=NMCeDfImxta1VEeBqqkP05xsuBK6YWFTd7Qj_bIEA2Y,8394
|
|
65
65
|
flwr/cli/utils.py,sha256=emMUdthvoHBTB0iGQp-oFBmA5wV46lw3y3FmfXQPCsc,4500
|
|
66
66
|
flwr/client/__init__.py,sha256=DGDoO0AEAfz-0CUFmLdyUUweAS64-07AOnmDfWUefK4,1192
|
|
67
|
-
flwr/client/app.py,sha256=
|
|
67
|
+
flwr/client/app.py,sha256=fEUTXz_uNwZe-otCbUf5F3sJozGfvkrMNS3u7DE6sOo,32808
|
|
68
68
|
flwr/client/client.py,sha256=gy6WVlMUFAp8oevN4xpQPX30vPOIYGVqdbuFlTWkyG4,9080
|
|
69
69
|
flwr/client/client_app.py,sha256=cTig-N00YzTucbo9zNi6I21J8PlbflU_8J_f5CI-Wpw,10390
|
|
70
70
|
flwr/client/clientapp/__init__.py,sha256=kZqChGnTChQ1WGSUkIlW2S5bc0d0mzDubCAmZUGRpEY,800
|
|
@@ -98,12 +98,12 @@ flwr/client/rest_client/__init__.py,sha256=5KGlp7pjc1dhNRkKlaNtUfQmg8wrRFh9lS3P3
|
|
|
98
98
|
flwr/client/rest_client/connection.py,sha256=k-RqgUFqidACAGlMFPIUM8aawXI5h2LvKUri2OAK7Bg,12817
|
|
99
99
|
flwr/client/run_info_store.py,sha256=SKpvq8vvNJgB7LDd-DLMndH99kNRztg2UXvnMh8fOEU,4005
|
|
100
100
|
flwr/client/supernode/__init__.py,sha256=SUhWOzcgXRNXk1V9UgB5-FaWukqqrOEajVUHEcPkwyQ,865
|
|
101
|
-
flwr/client/supernode/app.py,sha256=
|
|
101
|
+
flwr/client/supernode/app.py,sha256=JN24tRBHLbFJ0KeCTA8eS24KUJHCl9J2xGwWjyPQ7Vg,12239
|
|
102
102
|
flwr/client/typing.py,sha256=dxoTBnTMfqXr5J7G3y-uNjqxYCddvxhu89spfj4Lm2U,1048
|
|
103
103
|
flwr/common/__init__.py,sha256=TVaoFEJE158aui1TPZQiJCDZX4RNHRyI8I55VC80HhI,3901
|
|
104
104
|
flwr/common/address.py,sha256=7kM2Rqjw86-c8aKwAvrXerWqznnVv4TFJ62aSAeTn10,3017
|
|
105
105
|
flwr/common/config.py,sha256=nYA1vjiiqSWx5JjSdlQd1i_0N_Dh9kEGUse1Qze3JMs,7803
|
|
106
|
-
flwr/common/constant.py,sha256=
|
|
106
|
+
flwr/common/constant.py,sha256=iv2O8vQdrIqsGy-RFluRDd0R0oaqWO046KPm14yPzw0,4376
|
|
107
107
|
flwr/common/context.py,sha256=5Bd9RCrhLkYZOVR7vr97OVhzVBHQkS1fUsYiIKTwpxU,2239
|
|
108
108
|
flwr/common/date.py,sha256=OcQuwpb2HxcblTqYm6H223ufop5UZw5N_fzalbpOVzY,891
|
|
109
109
|
flwr/common/differential_privacy.py,sha256=XwcJ3rWr8S8BZUocc76vLSJAXIf6OHnWkBV6-xlIRuw,6106
|
|
@@ -135,7 +135,7 @@ flwr/common/secure_aggregation/secaggplus_constants.py,sha256=9MF-oQh62uD7rt9VeN
|
|
|
135
135
|
flwr/common/secure_aggregation/secaggplus_utils.py,sha256=o7IhHH6J9xqinhQy3TdPgQpoj1XyEpyv3OQFyx81RVQ,3193
|
|
136
136
|
flwr/common/serde.py,sha256=74nN5uqASdqfykSWPOhaTJARA07Iznyg3Nyr-dh-uy4,29918
|
|
137
137
|
flwr/common/telemetry.py,sha256=PvdlipCPYciqEgmXRwQ1HklP1uyECcNqt9HTBzthmAg,8904
|
|
138
|
-
flwr/common/typing.py,sha256=
|
|
138
|
+
flwr/common/typing.py,sha256=fS_KmVdg0c1B87yMnccIPfjBzQ3CTRwYJcaWfmvZzEA,5103
|
|
139
139
|
flwr/common/version.py,sha256=tCcl_FvxVK206C1dxIJCs4TjL06WmyaODBP19FRHE1c,1324
|
|
140
140
|
flwr/proto/__init__.py,sha256=hbY7JYakwZwCkYgCNlmHdc8rtvfoJbAZLalMdc--CGc,683
|
|
141
141
|
flwr/proto/clientappio_pb2.py,sha256=Y3PMv-JMaBGehpslgbvGY6l2u5vNpfCTFWu-fmAmBJ4,3703
|
|
@@ -200,7 +200,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
|
|
|
200
200
|
flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
|
|
201
201
|
flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
202
202
|
flwr/server/__init__.py,sha256=cEg1oecBu4cKB69iJCqWEylC8b5XW47bl7rQiJsdTvM,1528
|
|
203
|
-
flwr/server/app.py,sha256=
|
|
203
|
+
flwr/server/app.py,sha256=qChpmZm68wCkXsi9UgNGXQDRxTAm8klrH-66A9Oxsus,28255
|
|
204
204
|
flwr/server/client_manager.py,sha256=7Ese0tgrH-i-ms363feYZJKwB8gWnXSmg_hYF2Bju4U,6227
|
|
205
205
|
flwr/server/client_proxy.py,sha256=4G-oTwhb45sfWLx2uZdcXD98IZwdTS6F88xe3akCdUg,2399
|
|
206
206
|
flwr/server/compat/__init__.py,sha256=VxnJtJyOjNFQXMNi9hIuzNlZM5n0Hj1p3aq_Pm2udw4,892
|
|
@@ -219,7 +219,7 @@ flwr/server/server.py,sha256=1ZsFEptmAV-L2vP2etNC9Ed5CLSxpuKzUFkAPQ4l5Xc,17893
|
|
|
219
219
|
flwr/server/server_app.py,sha256=RsgS6PRS5Z74cMUAHzsm8r3LWddwn00MjRs6rlacHt8,6297
|
|
220
220
|
flwr/server/server_config.py,sha256=CZaHVAsMvGLjpWVcLPkiYxgJN4xfIyAiUrCI3fETKY4,1349
|
|
221
221
|
flwr/server/serverapp/__init__.py,sha256=L0K-94UDdTyEZ8LDtYybGIIIv3HW6AhSVjXMUfYJQnQ,800
|
|
222
|
-
flwr/server/serverapp/app.py,sha256=
|
|
222
|
+
flwr/server/serverapp/app.py,sha256=qrkJVCORJbDQN0I5JR_7t26GGwLLlApELQBWdbgButo,2353
|
|
223
223
|
flwr/server/serverapp_components.py,sha256=-IV_CitOfrJclJj2jNdbN1Q65PyFmtKtrTIg1hc6WQw,2118
|
|
224
224
|
flwr/server/strategy/__init__.py,sha256=tQer2SwjDnvgFFuJMZM-S01Z615N5XK6MaCvpm4BMU0,2836
|
|
225
225
|
flwr/server/strategy/aggregate.py,sha256=iFZ8lp7PV_a2m9kywV-FK0iM33ofxavOs5TIaEQY8nU,13961
|
|
@@ -274,11 +274,11 @@ flwr/server/superlink/fleet/vce/backend/backend.py,sha256=LBAQxnbfPAphVOVIvYMj0Q
|
|
|
274
274
|
flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=7kB3re3mR53b7E6L6DPSioTSKD3YGtS3uJsPD7Hn2Fw,7155
|
|
275
275
|
flwr/server/superlink/fleet/vce/vce_api.py,sha256=VL6e_Jwf4uxA-X1EelxJZMv6Eji-_p2J9D0MdHG10a4,13029
|
|
276
276
|
flwr/server/superlink/linkstate/__init__.py,sha256=v-2JyJlCB3qyhMNwMjmcNVOq4rkooqFU0LHH8Zo1jls,1064
|
|
277
|
-
flwr/server/superlink/linkstate/in_memory_linkstate.py,sha256=
|
|
278
|
-
flwr/server/superlink/linkstate/linkstate.py,sha256=
|
|
277
|
+
flwr/server/superlink/linkstate/in_memory_linkstate.py,sha256=PCN3O96GTR4-f_EhzTo8wqcerVTvXoPOwEjNyrKsQJ8,18830
|
|
278
|
+
flwr/server/superlink/linkstate/linkstate.py,sha256=2vGG_XkLM4OsAFLMBDqml9ZV90eAYNtnbIzhlnHRSNU,9346
|
|
279
279
|
flwr/server/superlink/linkstate/linkstate_factory.py,sha256=ISSMjDlwuN7swxjOeYlTNpI_kuZ8PGkMcJnf1dbhUSE,2069
|
|
280
|
-
flwr/server/superlink/linkstate/sqlite_linkstate.py,sha256
|
|
281
|
-
flwr/server/superlink/linkstate/utils.py,sha256=
|
|
280
|
+
flwr/server/superlink/linkstate/sqlite_linkstate.py,sha256=aH7XJ19QRB9KfhbBn45M9XunZXYkAqL9Jr6KvqjQwk0,39547
|
|
281
|
+
flwr/server/superlink/linkstate/utils.py,sha256=PBPsCruHLZQC2N71-ZWue5zqs1tkv6ULgSzpQJWKkro,6481
|
|
282
282
|
flwr/server/typing.py,sha256=5kaRLZuxTEse9A0g7aVna2VhYxU3wTq1f3d3mtw7kXs,1019
|
|
283
283
|
flwr/server/utils/__init__.py,sha256=pltsPHJoXmUIr3utjwwYxu7_ZAGy5u4MVHzv9iA5Un8,908
|
|
284
284
|
flwr/server/utils/tensorboard.py,sha256=gEBD8w_5uaIfp5aw5RYH66lYZpd_SfkObHQ7eDd9MUk,5466
|
|
@@ -295,7 +295,7 @@ flwr/simulation/ray_transport/__init__.py,sha256=wzcEEwUUlulnXsg6raCA1nGpP3LlAQD
|
|
|
295
295
|
flwr/simulation/ray_transport/ray_actor.py,sha256=9-XBguAm5IFqm2ddPFsQtnuuFN6lzqdb00SnCxGUGBo,18996
|
|
296
296
|
flwr/simulation/ray_transport/ray_client_proxy.py,sha256=2vjOKoom3B74C6XU-jC3N6DwYmsLdB-lmkHZ_Xrv96o,7367
|
|
297
297
|
flwr/simulation/ray_transport/utils.py,sha256=TYdtfg1P9VfTdLMOJlifInGpxWHYs9UfUqIv2wfkRLA,2392
|
|
298
|
-
flwr/simulation/run_simulation.py,sha256=
|
|
298
|
+
flwr/simulation/run_simulation.py,sha256=W3wGFXlrrTW7f1Zl1xWxtGRd2Vui7gXfIVO10a3eX8c,23217
|
|
299
299
|
flwr/superexec/__init__.py,sha256=fcj366jh4RFby_vDwLroU4kepzqbnJgseZD_jUr_Mko,715
|
|
300
300
|
flwr/superexec/app.py,sha256=KynGHvQjQpVfqydAaVHizbE3GbFFFZjOQQZleMt3SH0,6827
|
|
301
301
|
flwr/superexec/deployment.py,sha256=TbzOAAaY2sNt7O516w1GS6N5xvt0UV-dML74O6WA2O4,6344
|
|
@@ -303,8 +303,8 @@ flwr/superexec/exec_grpc.py,sha256=ZPq7EP55Vwj0kRcLVuTCokFqfIgBk-7YmDykZoMKi-c,1
|
|
|
303
303
|
flwr/superexec/exec_servicer.py,sha256=TRpwPVl7eI0Y_xlCY6DmVpAo0yFU1gLwzyIeqFw9pyk,4746
|
|
304
304
|
flwr/superexec/executor.py,sha256=-5J-ZLs-uArro3T2pCq0YQRC65cs18M888nufzdYE4E,2375
|
|
305
305
|
flwr/superexec/simulation.py,sha256=3z9mFhSw29Fup4RwV8ucr5eCrXI3urILWDrdC1sogik,7424
|
|
306
|
-
flwr_nightly-1.13.0.
|
|
307
|
-
flwr_nightly-1.13.0.
|
|
308
|
-
flwr_nightly-1.13.0.
|
|
309
|
-
flwr_nightly-1.13.0.
|
|
310
|
-
flwr_nightly-1.13.0.
|
|
306
|
+
flwr_nightly-1.13.0.dev20241023.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
307
|
+
flwr_nightly-1.13.0.dev20241023.dist-info/METADATA,sha256=nWx39T1F-7bEhxqNK3UgdAaMPIEB8CxTkKxY1cLDa6w,15618
|
|
308
|
+
flwr_nightly-1.13.0.dev20241023.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
309
|
+
flwr_nightly-1.13.0.dev20241023.dist-info/entry_points.txt,sha256=FxJQ96pmcNF2OvkTH6XF-Ip2PNrHvykjArkvkjQC7Mk,486
|
|
310
|
+
flwr_nightly-1.13.0.dev20241023.dist-info/RECORD,,
|
{flwr_nightly-1.13.0.dev20241022.dist-info → flwr_nightly-1.13.0.dev20241023.dist-info}/LICENSE
RENAMED
|
File without changes
|
{flwr_nightly-1.13.0.dev20241022.dist-info → flwr_nightly-1.13.0.dev20241023.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|