syft-flwr 0.2.0__tar.gz → 0.2.2__tar.gz
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 syft-flwr might be problematic. Click here for more details.
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/PKG-INFO +5 -4
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/README.md +2 -1
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/pyproject.toml +7 -3
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/__init__.py +1 -1
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/flower_client.py +2 -4
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/grid.py +77 -13
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/run.py +2 -1
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/utils.py +35 -1
- syft_flwr-0.2.0/src/syft_flwr/flwr_compatibility.py +0 -121
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/.gitignore +0 -0
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/LICENSE +0 -0
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/bootstrap.py +0 -0
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/cli.py +0 -0
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/config.py +0 -0
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/consts.py +0 -0
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/flower_server.py +0 -0
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/mounts.py +0 -0
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/run_simulation.py +0 -0
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/serde.py +0 -0
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/strategy/__init__.py +0 -0
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/strategy/fedavg.py +0 -0
- {syft_flwr-0.2.0 → syft_flwr-0.2.2}/src/syft_flwr/templates/main.py.tpl +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: syft-flwr
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: syft_flwr is an open source framework that facilitate federated learning projects using Flower over the SyftBox protocol
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.10
|
|
7
7
|
Requires-Dist: flwr-datasets[vision]>=0.5.0
|
|
8
|
-
Requires-Dist: flwr[simulation]
|
|
8
|
+
Requires-Dist: flwr[simulation]==1.21.0
|
|
9
9
|
Requires-Dist: loguru>=0.7.3
|
|
10
10
|
Requires-Dist: safetensors>=0.6.2
|
|
11
|
-
Requires-Dist: syft-rds>=0.2.
|
|
11
|
+
Requires-Dist: syft-rds>=0.2.2
|
|
12
12
|
Requires-Dist: tomli-w>=1.2.0
|
|
13
13
|
Requires-Dist: tomli>=2.2.1
|
|
14
14
|
Requires-Dist: typing-extensions>=4.13.0
|
|
@@ -23,4 +23,5 @@ Description-Content-Type: text/markdown
|
|
|
23
23
|
## Example Usages
|
|
24
24
|
Please look at the `notebooks/` folder for example use cases:
|
|
25
25
|
- [FL diabetes prediction](notebooks/fl-diabetes-prediction/README.md) shows how to train a federated model over distributed machines for multiple rounds
|
|
26
|
-
- [Federated analytics](notebooks/federated-analytics-diabetes/README.md) shows how to query statistics from private datasets from distributed machines and then aggregate them
|
|
26
|
+
- [Federated analytics](notebooks/federated-analytics-diabetes/README.md) shows how to query statistics from private datasets from distributed machines and then aggregate them
|
|
27
|
+
- [FedRAG (Federated RAG)](notebooks/fedrag/README.md) demonstrates privacy-preserving question answering using Retrieval Augmented Generation across distributed document sources with remote data science workflow
|
|
@@ -7,4 +7,5 @@
|
|
|
7
7
|
## Example Usages
|
|
8
8
|
Please look at the `notebooks/` folder for example use cases:
|
|
9
9
|
- [FL diabetes prediction](notebooks/fl-diabetes-prediction/README.md) shows how to train a federated model over distributed machines for multiple rounds
|
|
10
|
-
- [Federated analytics](notebooks/federated-analytics-diabetes/README.md) shows how to query statistics from private datasets from distributed machines and then aggregate them
|
|
10
|
+
- [Federated analytics](notebooks/federated-analytics-diabetes/README.md) shows how to query statistics from private datasets from distributed machines and then aggregate them
|
|
11
|
+
- [FedRAG (Federated RAG)](notebooks/fedrag/README.md) demonstrates privacy-preserving question answering using Retrieval Augmented Generation across distributed document sources with remote data science workflow
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "syft-flwr"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2"
|
|
4
4
|
description = "syft_flwr is an open source framework that facilitate federated learning projects using Flower over the SyftBox protocol"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
7
|
dependencies = [
|
|
8
|
-
"syft-rds>=0.2.
|
|
9
|
-
"flwr[simulation]
|
|
8
|
+
"syft-rds>=0.2.2",
|
|
9
|
+
"flwr[simulation]==1.21.0",
|
|
10
10
|
"flwr-datasets[vision]>=0.5.0",
|
|
11
11
|
"loguru>=0.7.3",
|
|
12
12
|
"safetensors>=0.6.2",
|
|
@@ -15,6 +15,10 @@ dependencies = [
|
|
|
15
15
|
"tomli-w>=1.2.0",
|
|
16
16
|
]
|
|
17
17
|
|
|
18
|
+
# [tool.uv.sources]
|
|
19
|
+
# syft-rds = { path = "../syft-data-science" } # for development
|
|
20
|
+
|
|
21
|
+
|
|
18
22
|
[project.scripts]
|
|
19
23
|
syft_flwr = "syft_flwr.cli:main"
|
|
20
24
|
|
|
@@ -6,14 +6,14 @@ from flwr.client import ClientApp
|
|
|
6
6
|
from flwr.common import Context
|
|
7
7
|
from flwr.common.constant import ErrorCode, MessageType
|
|
8
8
|
from flwr.common.message import Error, Message
|
|
9
|
+
from flwr.common.record import RecordDict
|
|
9
10
|
from loguru import logger
|
|
10
11
|
from syft_event import SyftEvents
|
|
11
12
|
from syft_event.types import Request
|
|
12
13
|
from typing_extensions import Optional, Union
|
|
13
14
|
|
|
14
|
-
from syft_flwr.flwr_compatibility import RecordDict, create_flwr_message
|
|
15
15
|
from syft_flwr.serde import bytes_to_flower_message, flower_message_to_bytes
|
|
16
|
-
from syft_flwr.utils import setup_client
|
|
16
|
+
from syft_flwr.utils import create_flwr_message, setup_client
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
class MessageHandler:
|
|
@@ -50,10 +50,8 @@ class MessageHandler:
|
|
|
50
50
|
content=RecordDict(),
|
|
51
51
|
reply_to=message,
|
|
52
52
|
message_type=message.metadata.message_type if message else MessageType.TASK,
|
|
53
|
-
src_node_id=message.metadata.dst_node_id if message else 0,
|
|
54
53
|
dst_node_id=message.metadata.src_node_id if message else 0,
|
|
55
54
|
group_id=message.metadata.group_id if message else "",
|
|
56
|
-
run_id=message.metadata.run_id if message else 0,
|
|
57
55
|
error=error,
|
|
58
56
|
)
|
|
59
57
|
error_bytes = flower_message_to_bytes(error_reply)
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import os
|
|
3
|
+
import random
|
|
3
4
|
import time
|
|
4
5
|
|
|
5
6
|
from flwr.common import ConfigRecord
|
|
6
7
|
from flwr.common.constant import MessageType
|
|
7
8
|
from flwr.common.message import Message
|
|
9
|
+
from flwr.common.record import RecordDict
|
|
8
10
|
from flwr.common.typing import Run
|
|
9
11
|
from flwr.proto.node_pb2 import Node # pylint: disable=E0611
|
|
12
|
+
from flwr.server.grid import Grid
|
|
10
13
|
from loguru import logger
|
|
11
14
|
from syft_core import Client
|
|
12
15
|
from syft_crypto import EncryptedPayload, decrypt_message
|
|
@@ -14,20 +17,15 @@ from syft_rpc import SyftResponse, rpc, rpc_db
|
|
|
14
17
|
from typing_extensions import Dict, Iterable, List, Optional, Tuple, cast
|
|
15
18
|
|
|
16
19
|
from syft_flwr.consts import SYFT_FLWR_ENCRYPTION_ENABLED
|
|
17
|
-
from syft_flwr.flwr_compatibility import (
|
|
18
|
-
Grid,
|
|
19
|
-
RecordDict,
|
|
20
|
-
check_reply_to_field,
|
|
21
|
-
create_flwr_message,
|
|
22
|
-
)
|
|
23
20
|
from syft_flwr.serde import bytes_to_flower_message, flower_message_to_bytes
|
|
24
|
-
from syft_flwr.utils import str_to_int
|
|
21
|
+
from syft_flwr.utils import check_reply_to_field, create_flwr_message, str_to_int
|
|
25
22
|
|
|
26
23
|
# this is what superlink super node do
|
|
27
24
|
AGGREGATOR_NODE_ID = 1
|
|
28
25
|
|
|
29
26
|
# env vars
|
|
30
27
|
SYFT_FLWR_MSG_TIMEOUT = "SYFT_FLWR_MSG_TIMEOUT"
|
|
28
|
+
SYFT_FLWR_POLL_INTERVAL = "SYFT_FLWR_POLL_INTERVAL"
|
|
31
29
|
|
|
32
30
|
|
|
33
31
|
class SyftGrid(Grid):
|
|
@@ -127,8 +125,6 @@ class SyftGrid(Grid):
|
|
|
127
125
|
dst_node_id=dst_node_id,
|
|
128
126
|
group_id=group_id,
|
|
129
127
|
ttl=ttl,
|
|
130
|
-
run_id=cast(Run, self._run).run_id,
|
|
131
|
-
src_node_id=self.node.node_id,
|
|
132
128
|
)
|
|
133
129
|
|
|
134
130
|
def get_node_ids(self) -> list[int]:
|
|
@@ -318,27 +314,91 @@ class SyftGrid(Grid):
|
|
|
318
314
|
|
|
319
315
|
return dest_datasite, url, msg_bytes
|
|
320
316
|
|
|
317
|
+
def _retry_with_backoff(
|
|
318
|
+
self,
|
|
319
|
+
func,
|
|
320
|
+
max_retries: int = 3,
|
|
321
|
+
initial_delay: float = 0.1,
|
|
322
|
+
context: str = "",
|
|
323
|
+
check_error=None,
|
|
324
|
+
):
|
|
325
|
+
"""Generic retry logic with exponential backoff and jitter.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
func: Function to retry
|
|
329
|
+
max_retries: Maximum number of retry attempts
|
|
330
|
+
initial_delay: Initial delay in seconds
|
|
331
|
+
context: Context string for logging
|
|
332
|
+
check_error: Optional function to check if error is retryable
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
Result of func if successful
|
|
336
|
+
|
|
337
|
+
Raises:
|
|
338
|
+
Last exception if all retries fail
|
|
339
|
+
"""
|
|
340
|
+
for attempt in range(max_retries):
|
|
341
|
+
try:
|
|
342
|
+
return func()
|
|
343
|
+
except Exception as e:
|
|
344
|
+
is_retryable = check_error(e) if check_error else True
|
|
345
|
+
if is_retryable and attempt < max_retries - 1:
|
|
346
|
+
jitter = random.uniform(0, 0.05)
|
|
347
|
+
delay = initial_delay * (2**attempt) + jitter
|
|
348
|
+
logger.debug(
|
|
349
|
+
f"{context} failed (attempt {attempt + 1}/{max_retries}): {e}. "
|
|
350
|
+
f"Retrying in {delay:.3f}s"
|
|
351
|
+
)
|
|
352
|
+
time.sleep(delay)
|
|
353
|
+
else:
|
|
354
|
+
raise
|
|
355
|
+
|
|
356
|
+
def _save_future_with_retry(self, future, dest_datasite: str) -> bool:
|
|
357
|
+
"""Save future to database with retry logic for database locks.
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
True if saved successfully, False if failed after retries
|
|
361
|
+
"""
|
|
362
|
+
try:
|
|
363
|
+
self._retry_with_backoff(
|
|
364
|
+
func=lambda: rpc_db.save_future(
|
|
365
|
+
future=future, namespace=self.app_name, client=self._client
|
|
366
|
+
),
|
|
367
|
+
context=f"Database save for {dest_datasite}",
|
|
368
|
+
check_error=lambda e: "database is locked" in str(e).lower(),
|
|
369
|
+
)
|
|
370
|
+
return True
|
|
371
|
+
except Exception as e:
|
|
372
|
+
logger.warning(
|
|
373
|
+
f"⚠️ Failed to save future to database for {dest_datasite}: {e}. "
|
|
374
|
+
f"Message sent but future not persisted."
|
|
375
|
+
)
|
|
376
|
+
return False
|
|
377
|
+
|
|
321
378
|
def _send_encrypted_message(
|
|
322
379
|
self, url: str, msg_bytes: bytes, dest_datasite: str, msg: Message
|
|
323
380
|
) -> Optional[str]:
|
|
324
381
|
"""Send an encrypted message and return future ID if successful."""
|
|
325
382
|
try:
|
|
383
|
+
# Send encrypted message
|
|
326
384
|
future = rpc.send(
|
|
327
385
|
url=url,
|
|
328
386
|
body=base64.b64encode(msg_bytes).decode("utf-8"),
|
|
329
387
|
client=self._client,
|
|
330
388
|
encrypt=True,
|
|
331
389
|
)
|
|
390
|
+
|
|
332
391
|
logger.debug(
|
|
333
392
|
f"🔐 Pushed ENCRYPTED message to {dest_datasite} at {url} "
|
|
334
393
|
f"with metadata {msg.metadata}; size {len(msg_bytes) / 1024 / 1024:.2f} MB"
|
|
335
394
|
)
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
)
|
|
395
|
+
|
|
396
|
+
# Save future to database (non-critical - log warning if fails)
|
|
397
|
+
self._save_future_with_retry(future, dest_datasite)
|
|
339
398
|
return future.id
|
|
340
399
|
|
|
341
400
|
except (KeyError, ValueError) as e:
|
|
401
|
+
# Encryption setup errors - don't retry or fallback
|
|
342
402
|
error_type = (
|
|
343
403
|
"Encryption key" if isinstance(e, KeyError) else "Encryption parameter"
|
|
344
404
|
)
|
|
@@ -349,6 +409,7 @@ class SyftGrid(Grid):
|
|
|
349
409
|
return None
|
|
350
410
|
|
|
351
411
|
except Exception as e:
|
|
412
|
+
# Other errors - fallback to unencrypted
|
|
352
413
|
logger.warning(
|
|
353
414
|
f"⚠️ Encryption failed for {dest_datasite}: {e}. "
|
|
354
415
|
f"Falling back to unencrypted transmission"
|
|
@@ -382,6 +443,9 @@ class SyftGrid(Grid):
|
|
|
382
443
|
responses = {}
|
|
383
444
|
pending_ids = msg_ids.copy()
|
|
384
445
|
|
|
446
|
+
# Get polling interval from environment or use default
|
|
447
|
+
poll_interval = float(os.environ.get(SYFT_FLWR_POLL_INTERVAL, "3"))
|
|
448
|
+
|
|
385
449
|
while pending_ids and (timeout is None or time.time() < end_time):
|
|
386
450
|
# Pull available messages
|
|
387
451
|
batch = self.pull_messages(pending_ids)
|
|
@@ -389,7 +453,7 @@ class SyftGrid(Grid):
|
|
|
389
453
|
pending_ids.difference_update(batch.keys())
|
|
390
454
|
|
|
391
455
|
if pending_ids:
|
|
392
|
-
time.sleep(
|
|
456
|
+
time.sleep(poll_interval) # Configurable polling interval
|
|
393
457
|
|
|
394
458
|
# Log any missing responses
|
|
395
459
|
if pending_ids:
|
|
@@ -5,11 +5,12 @@ from uuid import uuid4
|
|
|
5
5
|
from flwr.client.client_app import LoadClientAppError
|
|
6
6
|
from flwr.common import Context
|
|
7
7
|
from flwr.common.object_ref import load_app
|
|
8
|
+
from flwr.common.record import RecordDict
|
|
8
9
|
from flwr.server.server_app import LoadServerAppError
|
|
10
|
+
|
|
9
11
|
from syft_flwr.config import load_flwr_pyproject
|
|
10
12
|
from syft_flwr.flower_client import syftbox_flwr_client
|
|
11
13
|
from syft_flwr.flower_server import syftbox_flwr_server
|
|
12
|
-
from syft_flwr.flwr_compatibility import RecordDict
|
|
13
14
|
from syft_flwr.run_simulation import run
|
|
14
15
|
|
|
15
16
|
__all__ = ["syftbox_run_flwr_client", "syftbox_run_flwr_server", "run"]
|
|
@@ -3,10 +3,13 @@ import re
|
|
|
3
3
|
import zlib
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
+
from flwr.common import Metadata
|
|
7
|
+
from flwr.common.message import Error, Message
|
|
8
|
+
from flwr.common.record import RecordDict
|
|
6
9
|
from loguru import logger
|
|
7
10
|
from syft_core import Client, SyftClientConfig
|
|
8
11
|
from syft_crypto.x3dh_bootstrap import ensure_bootstrap
|
|
9
|
-
from typing_extensions import Tuple
|
|
12
|
+
from typing_extensions import Optional, Tuple
|
|
10
13
|
|
|
11
14
|
from syft_flwr.consts import SYFT_FLWR_ENCRYPTION_ENABLED
|
|
12
15
|
|
|
@@ -79,3 +82,34 @@ def setup_client(app_name: str) -> Tuple[Client, bool, str]:
|
|
|
79
82
|
)
|
|
80
83
|
|
|
81
84
|
return client, encryption_enabled, f"flwr/{app_name}"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def check_reply_to_field(metadata: Metadata) -> bool:
|
|
88
|
+
"""Check if reply_to field is empty (Flower 1.17+ format)."""
|
|
89
|
+
return metadata.reply_to_message_id == ""
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def create_flwr_message(
|
|
93
|
+
content: RecordDict,
|
|
94
|
+
message_type: str,
|
|
95
|
+
dst_node_id: int,
|
|
96
|
+
group_id: str,
|
|
97
|
+
ttl: Optional[float] = None,
|
|
98
|
+
error: Optional[Error] = None,
|
|
99
|
+
reply_to: Optional[Message] = None,
|
|
100
|
+
) -> Message:
|
|
101
|
+
"""Create a Flower message (requires Flower >= 1.17)."""
|
|
102
|
+
if reply_to is not None:
|
|
103
|
+
if error is not None:
|
|
104
|
+
return Message(reply_to=reply_to, error=error)
|
|
105
|
+
return Message(content=content, reply_to=reply_to)
|
|
106
|
+
else:
|
|
107
|
+
if error is not None:
|
|
108
|
+
raise ValueError("Error and reply_to cannot both be None")
|
|
109
|
+
return Message(
|
|
110
|
+
content=content,
|
|
111
|
+
dst_node_id=dst_node_id,
|
|
112
|
+
message_type=message_type,
|
|
113
|
+
ttl=ttl,
|
|
114
|
+
group_id=group_id,
|
|
115
|
+
)
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import flwr
|
|
2
|
-
from flwr.common import Metadata
|
|
3
|
-
from flwr.common.message import Error, Message
|
|
4
|
-
from packaging.version import Version
|
|
5
|
-
from typing_extensions import Optional
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def flwr_later_than_1_17():
|
|
9
|
-
return Version(flwr.__version__) >= Version("1.17.0")
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
# Version-dependent imports
|
|
13
|
-
if flwr_later_than_1_17():
|
|
14
|
-
from flwr.common.record import RecordDict
|
|
15
|
-
from flwr.server.grid import Grid
|
|
16
|
-
else:
|
|
17
|
-
from flwr.common.record import RecordSet as RecordDict
|
|
18
|
-
from flwr.server.driver import Driver as Grid
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
__all__ = ["Grid", "RecordDict"]
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def check_reply_to_field(metadata: Metadata) -> bool:
|
|
25
|
-
"""Check if reply_to field is empty based on Flower version."""
|
|
26
|
-
if flwr_later_than_1_17():
|
|
27
|
-
return metadata.reply_to_message_id == ""
|
|
28
|
-
else:
|
|
29
|
-
return metadata.reply_to_message == ""
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def create_flwr_message(
|
|
33
|
-
content: RecordDict,
|
|
34
|
-
message_type: str,
|
|
35
|
-
src_node_id: int,
|
|
36
|
-
dst_node_id: int,
|
|
37
|
-
group_id: str,
|
|
38
|
-
run_id: int,
|
|
39
|
-
ttl: Optional[float] = None,
|
|
40
|
-
error: Optional[Error] = None,
|
|
41
|
-
reply_to: Optional[Message] = None,
|
|
42
|
-
) -> Message:
|
|
43
|
-
"""Create a Flower message with version-compatible parameters."""
|
|
44
|
-
if flwr_later_than_1_17():
|
|
45
|
-
return _create_message_v1_17_plus(
|
|
46
|
-
content,
|
|
47
|
-
message_type,
|
|
48
|
-
dst_node_id,
|
|
49
|
-
group_id,
|
|
50
|
-
ttl,
|
|
51
|
-
error,
|
|
52
|
-
reply_to,
|
|
53
|
-
)
|
|
54
|
-
else:
|
|
55
|
-
return _create_message_pre_v1_17(
|
|
56
|
-
content,
|
|
57
|
-
message_type,
|
|
58
|
-
src_node_id,
|
|
59
|
-
dst_node_id,
|
|
60
|
-
group_id,
|
|
61
|
-
run_id,
|
|
62
|
-
ttl,
|
|
63
|
-
error,
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def _create_message_v1_17_plus(
|
|
68
|
-
content: RecordDict,
|
|
69
|
-
message_type: str,
|
|
70
|
-
dst_node_id: int,
|
|
71
|
-
group_id: str,
|
|
72
|
-
ttl: Optional[float],
|
|
73
|
-
error: Optional[Error],
|
|
74
|
-
reply_to: Optional[Message],
|
|
75
|
-
) -> Message:
|
|
76
|
-
"""Create message for Flower version 1.17+."""
|
|
77
|
-
if reply_to is not None:
|
|
78
|
-
if error is not None:
|
|
79
|
-
return Message(reply_to=reply_to, error=error)
|
|
80
|
-
return Message(content=content, reply_to=reply_to)
|
|
81
|
-
else:
|
|
82
|
-
if error is not None:
|
|
83
|
-
raise ValueError("Error and reply_to cannot both be None")
|
|
84
|
-
return Message(
|
|
85
|
-
content=content,
|
|
86
|
-
dst_node_id=dst_node_id,
|
|
87
|
-
message_type=message_type,
|
|
88
|
-
ttl=ttl,
|
|
89
|
-
group_id=group_id,
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def _create_message_pre_v1_17(
|
|
94
|
-
content: RecordDict,
|
|
95
|
-
message_type: str,
|
|
96
|
-
src_node_id: int,
|
|
97
|
-
dst_node_id: int,
|
|
98
|
-
group_id: str,
|
|
99
|
-
run_id: int,
|
|
100
|
-
ttl: Optional[float],
|
|
101
|
-
error: Optional[Error],
|
|
102
|
-
) -> Message:
|
|
103
|
-
"""Create message for Flower versions before 1.17."""
|
|
104
|
-
from flwr.common import DEFAULT_TTL
|
|
105
|
-
|
|
106
|
-
ttl_ = DEFAULT_TTL if ttl is None else ttl
|
|
107
|
-
metadata = Metadata(
|
|
108
|
-
run_id=run_id,
|
|
109
|
-
message_id="", # Will be set when saving to file
|
|
110
|
-
src_node_id=src_node_id,
|
|
111
|
-
dst_node_id=dst_node_id,
|
|
112
|
-
reply_to_message="",
|
|
113
|
-
group_id=group_id,
|
|
114
|
-
ttl=ttl_,
|
|
115
|
-
message_type=message_type,
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
if error is not None:
|
|
119
|
-
return Message(metadata=metadata, error=error)
|
|
120
|
-
else:
|
|
121
|
-
return Message(metadata=metadata, content=content)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|