flwr-nightly 1.10.0.dev20240612__py3-none-any.whl → 1.10.0.dev20240619__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.

Files changed (53) hide show
  1. flwr/cli/build.py +3 -1
  2. flwr/cli/config_utils.py +53 -3
  3. flwr/cli/install.py +35 -20
  4. flwr/cli/run/run.py +39 -2
  5. flwr/client/__init__.py +1 -1
  6. flwr/client/app.py +22 -10
  7. flwr/client/grpc_adapter_client/__init__.py +15 -0
  8. flwr/client/grpc_adapter_client/connection.py +94 -0
  9. flwr/client/grpc_client/connection.py +5 -1
  10. flwr/client/grpc_rere_client/connection.py +8 -1
  11. flwr/client/grpc_rere_client/grpc_adapter.py +133 -0
  12. flwr/client/mod/__init__.py +3 -3
  13. flwr/client/rest_client/connection.py +9 -1
  14. flwr/client/supernode/app.py +140 -40
  15. flwr/common/__init__.py +12 -12
  16. flwr/common/config.py +71 -0
  17. flwr/common/constant.py +15 -0
  18. flwr/common/object_ref.py +39 -5
  19. flwr/common/record/__init__.py +1 -1
  20. flwr/common/telemetry.py +4 -0
  21. flwr/common/typing.py +9 -0
  22. flwr/proto/exec_pb2.py +34 -0
  23. flwr/proto/exec_pb2.pyi +55 -0
  24. flwr/proto/exec_pb2_grpc.py +101 -0
  25. flwr/proto/exec_pb2_grpc.pyi +41 -0
  26. flwr/proto/fab_pb2.py +30 -0
  27. flwr/proto/fab_pb2.pyi +56 -0
  28. flwr/proto/fab_pb2_grpc.py +4 -0
  29. flwr/proto/fab_pb2_grpc.pyi +4 -0
  30. flwr/server/__init__.py +2 -2
  31. flwr/server/app.py +62 -25
  32. flwr/server/run_serverapp.py +4 -2
  33. flwr/server/strategy/__init__.py +2 -2
  34. flwr/server/superlink/fleet/grpc_adapter/__init__.py +15 -0
  35. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +131 -0
  36. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +4 -0
  37. flwr/server/superlink/fleet/message_handler/message_handler.py +3 -3
  38. flwr/server/superlink/fleet/vce/vce_api.py +3 -1
  39. flwr/server/superlink/state/in_memory_state.py +8 -5
  40. flwr/server/superlink/state/sqlite_state.py +6 -3
  41. flwr/server/superlink/state/state.py +5 -4
  42. flwr/simulation/__init__.py +4 -1
  43. flwr/simulation/run_simulation.py +22 -0
  44. flwr/superexec/__init__.py +21 -0
  45. flwr/superexec/app.py +178 -0
  46. flwr/superexec/exec_grpc.py +51 -0
  47. flwr/superexec/exec_servicer.py +65 -0
  48. flwr/superexec/executor.py +54 -0
  49. {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240619.dist-info}/METADATA +1 -1
  50. {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240619.dist-info}/RECORD +53 -34
  51. {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240619.dist-info}/entry_points.txt +1 -0
  52. {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240619.dist-info}/LICENSE +0 -0
  53. {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240619.dist-info}/WHEEL +0 -0
flwr/proto/exec_pb2.py ADDED
@@ -0,0 +1,34 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: flwr/proto/exec.proto
4
+ # Protobuf Python Version: 4.25.0
5
+ """Generated protocol buffer code."""
6
+ from google.protobuf import descriptor as _descriptor
7
+ from google.protobuf import descriptor_pool as _descriptor_pool
8
+ from google.protobuf import symbol_database as _symbol_database
9
+ from google.protobuf.internal import builder as _builder
10
+ # @@protoc_insertion_point(imports)
11
+
12
+ _sym_db = _symbol_database.Default()
13
+
14
+
15
+
16
+
17
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15\x66lwr/proto/exec.proto\x12\nflwr.proto\"#\n\x0fStartRunRequest\x12\x10\n\x08\x66\x61\x62_file\x18\x01 \x01(\x0c\"\"\n\x10StartRunResponse\x12\x0e\n\x06run_id\x18\x01 \x01(\x12\"#\n\x11StreamLogsRequest\x12\x0e\n\x06run_id\x18\x01 \x01(\x12\"(\n\x12StreamLogsResponse\x12\x12\n\nlog_output\x18\x01 \x01(\t2\xa0\x01\n\x04\x45xec\x12G\n\x08StartRun\x12\x1b.flwr.proto.StartRunRequest\x1a\x1c.flwr.proto.StartRunResponse\"\x00\x12O\n\nStreamLogs\x12\x1d.flwr.proto.StreamLogsRequest\x1a\x1e.flwr.proto.StreamLogsResponse\"\x00\x30\x01\x62\x06proto3')
18
+
19
+ _globals = globals()
20
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
21
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'flwr.proto.exec_pb2', _globals)
22
+ if _descriptor._USE_C_DESCRIPTORS == False:
23
+ DESCRIPTOR._options = None
24
+ _globals['_STARTRUNREQUEST']._serialized_start=37
25
+ _globals['_STARTRUNREQUEST']._serialized_end=72
26
+ _globals['_STARTRUNRESPONSE']._serialized_start=74
27
+ _globals['_STARTRUNRESPONSE']._serialized_end=108
28
+ _globals['_STREAMLOGSREQUEST']._serialized_start=110
29
+ _globals['_STREAMLOGSREQUEST']._serialized_end=145
30
+ _globals['_STREAMLOGSRESPONSE']._serialized_start=147
31
+ _globals['_STREAMLOGSRESPONSE']._serialized_end=187
32
+ _globals['_EXEC']._serialized_start=190
33
+ _globals['_EXEC']._serialized_end=350
34
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,55 @@
1
+ """
2
+ @generated by mypy-protobuf. Do not edit manually!
3
+ isort:skip_file
4
+ """
5
+ import builtins
6
+ import google.protobuf.descriptor
7
+ import google.protobuf.message
8
+ import typing
9
+ import typing_extensions
10
+
11
+ DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
12
+
13
+ class StartRunRequest(google.protobuf.message.Message):
14
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
15
+ FAB_FILE_FIELD_NUMBER: builtins.int
16
+ fab_file: builtins.bytes
17
+ def __init__(self,
18
+ *,
19
+ fab_file: builtins.bytes = ...,
20
+ ) -> None: ...
21
+ def ClearField(self, field_name: typing_extensions.Literal["fab_file",b"fab_file"]) -> None: ...
22
+ global___StartRunRequest = StartRunRequest
23
+
24
+ class StartRunResponse(google.protobuf.message.Message):
25
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
26
+ RUN_ID_FIELD_NUMBER: builtins.int
27
+ run_id: builtins.int
28
+ def __init__(self,
29
+ *,
30
+ run_id: builtins.int = ...,
31
+ ) -> None: ...
32
+ def ClearField(self, field_name: typing_extensions.Literal["run_id",b"run_id"]) -> None: ...
33
+ global___StartRunResponse = StartRunResponse
34
+
35
+ class StreamLogsRequest(google.protobuf.message.Message):
36
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
37
+ RUN_ID_FIELD_NUMBER: builtins.int
38
+ run_id: builtins.int
39
+ def __init__(self,
40
+ *,
41
+ run_id: builtins.int = ...,
42
+ ) -> None: ...
43
+ def ClearField(self, field_name: typing_extensions.Literal["run_id",b"run_id"]) -> None: ...
44
+ global___StreamLogsRequest = StreamLogsRequest
45
+
46
+ class StreamLogsResponse(google.protobuf.message.Message):
47
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
48
+ LOG_OUTPUT_FIELD_NUMBER: builtins.int
49
+ log_output: typing.Text
50
+ def __init__(self,
51
+ *,
52
+ log_output: typing.Text = ...,
53
+ ) -> None: ...
54
+ def ClearField(self, field_name: typing_extensions.Literal["log_output",b"log_output"]) -> None: ...
55
+ global___StreamLogsResponse = StreamLogsResponse
@@ -0,0 +1,101 @@
1
+ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2
+ """Client and server classes corresponding to protobuf-defined services."""
3
+ import grpc
4
+
5
+ from flwr.proto import exec_pb2 as flwr_dot_proto_dot_exec__pb2
6
+
7
+
8
+ class ExecStub(object):
9
+ """Missing associated documentation comment in .proto file."""
10
+
11
+ def __init__(self, channel):
12
+ """Constructor.
13
+
14
+ Args:
15
+ channel: A grpc.Channel.
16
+ """
17
+ self.StartRun = channel.unary_unary(
18
+ '/flwr.proto.Exec/StartRun',
19
+ request_serializer=flwr_dot_proto_dot_exec__pb2.StartRunRequest.SerializeToString,
20
+ response_deserializer=flwr_dot_proto_dot_exec__pb2.StartRunResponse.FromString,
21
+ )
22
+ self.StreamLogs = channel.unary_stream(
23
+ '/flwr.proto.Exec/StreamLogs',
24
+ request_serializer=flwr_dot_proto_dot_exec__pb2.StreamLogsRequest.SerializeToString,
25
+ response_deserializer=flwr_dot_proto_dot_exec__pb2.StreamLogsResponse.FromString,
26
+ )
27
+
28
+
29
+ class ExecServicer(object):
30
+ """Missing associated documentation comment in .proto file."""
31
+
32
+ def StartRun(self, request, context):
33
+ """Start run upon request
34
+ """
35
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
36
+ context.set_details('Method not implemented!')
37
+ raise NotImplementedError('Method not implemented!')
38
+
39
+ def StreamLogs(self, request, context):
40
+ """Start log stream upon request
41
+ """
42
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
43
+ context.set_details('Method not implemented!')
44
+ raise NotImplementedError('Method not implemented!')
45
+
46
+
47
+ def add_ExecServicer_to_server(servicer, server):
48
+ rpc_method_handlers = {
49
+ 'StartRun': grpc.unary_unary_rpc_method_handler(
50
+ servicer.StartRun,
51
+ request_deserializer=flwr_dot_proto_dot_exec__pb2.StartRunRequest.FromString,
52
+ response_serializer=flwr_dot_proto_dot_exec__pb2.StartRunResponse.SerializeToString,
53
+ ),
54
+ 'StreamLogs': grpc.unary_stream_rpc_method_handler(
55
+ servicer.StreamLogs,
56
+ request_deserializer=flwr_dot_proto_dot_exec__pb2.StreamLogsRequest.FromString,
57
+ response_serializer=flwr_dot_proto_dot_exec__pb2.StreamLogsResponse.SerializeToString,
58
+ ),
59
+ }
60
+ generic_handler = grpc.method_handlers_generic_handler(
61
+ 'flwr.proto.Exec', rpc_method_handlers)
62
+ server.add_generic_rpc_handlers((generic_handler,))
63
+
64
+
65
+ # This class is part of an EXPERIMENTAL API.
66
+ class Exec(object):
67
+ """Missing associated documentation comment in .proto file."""
68
+
69
+ @staticmethod
70
+ def StartRun(request,
71
+ target,
72
+ options=(),
73
+ channel_credentials=None,
74
+ call_credentials=None,
75
+ insecure=False,
76
+ compression=None,
77
+ wait_for_ready=None,
78
+ timeout=None,
79
+ metadata=None):
80
+ return grpc.experimental.unary_unary(request, target, '/flwr.proto.Exec/StartRun',
81
+ flwr_dot_proto_dot_exec__pb2.StartRunRequest.SerializeToString,
82
+ flwr_dot_proto_dot_exec__pb2.StartRunResponse.FromString,
83
+ options, channel_credentials,
84
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
85
+
86
+ @staticmethod
87
+ def StreamLogs(request,
88
+ target,
89
+ options=(),
90
+ channel_credentials=None,
91
+ call_credentials=None,
92
+ insecure=False,
93
+ compression=None,
94
+ wait_for_ready=None,
95
+ timeout=None,
96
+ metadata=None):
97
+ return grpc.experimental.unary_stream(request, target, '/flwr.proto.Exec/StreamLogs',
98
+ flwr_dot_proto_dot_exec__pb2.StreamLogsRequest.SerializeToString,
99
+ flwr_dot_proto_dot_exec__pb2.StreamLogsResponse.FromString,
100
+ options, channel_credentials,
101
+ insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@@ -0,0 +1,41 @@
1
+ """
2
+ @generated by mypy-protobuf. Do not edit manually!
3
+ isort:skip_file
4
+ """
5
+ import abc
6
+ import flwr.proto.exec_pb2
7
+ import grpc
8
+ import typing
9
+
10
+ class ExecStub:
11
+ def __init__(self, channel: grpc.Channel) -> None: ...
12
+ StartRun: grpc.UnaryUnaryMultiCallable[
13
+ flwr.proto.exec_pb2.StartRunRequest,
14
+ flwr.proto.exec_pb2.StartRunResponse]
15
+ """Start run upon request"""
16
+
17
+ StreamLogs: grpc.UnaryStreamMultiCallable[
18
+ flwr.proto.exec_pb2.StreamLogsRequest,
19
+ flwr.proto.exec_pb2.StreamLogsResponse]
20
+ """Start log stream upon request"""
21
+
22
+
23
+ class ExecServicer(metaclass=abc.ABCMeta):
24
+ @abc.abstractmethod
25
+ def StartRun(self,
26
+ request: flwr.proto.exec_pb2.StartRunRequest,
27
+ context: grpc.ServicerContext,
28
+ ) -> flwr.proto.exec_pb2.StartRunResponse:
29
+ """Start run upon request"""
30
+ pass
31
+
32
+ @abc.abstractmethod
33
+ def StreamLogs(self,
34
+ request: flwr.proto.exec_pb2.StreamLogsRequest,
35
+ context: grpc.ServicerContext,
36
+ ) -> typing.Iterator[flwr.proto.exec_pb2.StreamLogsResponse]:
37
+ """Start log stream upon request"""
38
+ pass
39
+
40
+
41
+ def add_ExecServicer_to_server(servicer: ExecServicer, server: grpc.Server) -> None: ...
flwr/proto/fab_pb2.py ADDED
@@ -0,0 +1,30 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # source: flwr/proto/fab.proto
4
+ # Protobuf Python Version: 4.25.0
5
+ """Generated protocol buffer code."""
6
+ from google.protobuf import descriptor as _descriptor
7
+ from google.protobuf import descriptor_pool as _descriptor_pool
8
+ from google.protobuf import symbol_database as _symbol_database
9
+ from google.protobuf.internal import builder as _builder
10
+ # @@protoc_insertion_point(imports)
11
+
12
+ _sym_db = _symbol_database.Default()
13
+
14
+
15
+
16
+
17
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x66lwr/proto/fab.proto\x12\nflwr.proto\"$\n\x03\x46\x61\x62\x12\x0c\n\x04hash\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\x0c\"\x1d\n\rGetFabRequest\x12\x0c\n\x04hash\x18\x01 \x01(\t\".\n\x0eGetFabResponse\x12\x1c\n\x03\x66\x61\x62\x18\x01 \x01(\x0b\x32\x0f.flwr.proto.Fabb\x06proto3')
18
+
19
+ _globals = globals()
20
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
21
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'flwr.proto.fab_pb2', _globals)
22
+ if _descriptor._USE_C_DESCRIPTORS == False:
23
+ DESCRIPTOR._options = None
24
+ _globals['_FAB']._serialized_start=36
25
+ _globals['_FAB']._serialized_end=72
26
+ _globals['_GETFABREQUEST']._serialized_start=74
27
+ _globals['_GETFABREQUEST']._serialized_end=103
28
+ _globals['_GETFABRESPONSE']._serialized_start=105
29
+ _globals['_GETFABRESPONSE']._serialized_end=151
30
+ # @@protoc_insertion_point(module_scope)
flwr/proto/fab_pb2.pyi ADDED
@@ -0,0 +1,56 @@
1
+ """
2
+ @generated by mypy-protobuf. Do not edit manually!
3
+ isort:skip_file
4
+ """
5
+ import builtins
6
+ import google.protobuf.descriptor
7
+ import google.protobuf.message
8
+ import typing
9
+ import typing_extensions
10
+
11
+ DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
12
+
13
+ class Fab(google.protobuf.message.Message):
14
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
15
+ HASH_FIELD_NUMBER: builtins.int
16
+ CONTENT_FIELD_NUMBER: builtins.int
17
+ hash: typing.Text
18
+ """This field is the hash of the data field. It is used to identify the data.
19
+ The hash is calculated using the SHA-256 algorithm and is represented as a
20
+ hex string (sha256hex).
21
+ """
22
+
23
+ content: builtins.bytes
24
+ """This field contains the fab file contents a one bytes blob."""
25
+
26
+ def __init__(self,
27
+ *,
28
+ hash: typing.Text = ...,
29
+ content: builtins.bytes = ...,
30
+ ) -> None: ...
31
+ def ClearField(self, field_name: typing_extensions.Literal["content",b"content","hash",b"hash"]) -> None: ...
32
+ global___Fab = Fab
33
+
34
+ class GetFabRequest(google.protobuf.message.Message):
35
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
36
+ HASH_FIELD_NUMBER: builtins.int
37
+ hash: typing.Text
38
+ def __init__(self,
39
+ *,
40
+ hash: typing.Text = ...,
41
+ ) -> None: ...
42
+ def ClearField(self, field_name: typing_extensions.Literal["hash",b"hash"]) -> None: ...
43
+ global___GetFabRequest = GetFabRequest
44
+
45
+ class GetFabResponse(google.protobuf.message.Message):
46
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
47
+ FAB_FIELD_NUMBER: builtins.int
48
+ @property
49
+ def fab(self) -> global___Fab: ...
50
+ def __init__(self,
51
+ *,
52
+ fab: typing.Optional[global___Fab] = ...,
53
+ ) -> None: ...
54
+ def HasField(self, field_name: typing_extensions.Literal["fab",b"fab"]) -> builtins.bool: ...
55
+ def ClearField(self, field_name: typing_extensions.Literal["fab",b"fab"]) -> None: ...
56
+ global___GetFabResponse = GetFabResponse
@@ -0,0 +1,4 @@
1
+ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
2
+ """Client and server classes corresponding to protobuf-defined services."""
3
+ import grpc
4
+
@@ -0,0 +1,4 @@
1
+ """
2
+ @generated by mypy-protobuf. Do not edit manually!
3
+ isort:skip_file
4
+ """
flwr/server/__init__.py CHANGED
@@ -34,12 +34,12 @@ __all__ = [
34
34
  "Driver",
35
35
  "History",
36
36
  "LegacyContext",
37
- "run_server_app",
38
- "run_superlink",
39
37
  "Server",
40
38
  "ServerApp",
41
39
  "ServerConfig",
42
40
  "SimpleClientManager",
41
+ "run_server_app",
42
+ "run_superlink",
43
43
  "start_server",
44
44
  "strategy",
45
45
  "workflow",
flwr/server/app.py CHANGED
@@ -36,6 +36,7 @@ from flwr.common import GRPC_MAX_MESSAGE_LENGTH, EventType, event
36
36
  from flwr.common.address import parse_address
37
37
  from flwr.common.constant import (
38
38
  MISSING_EXTRA_REST,
39
+ TRANSPORT_TYPE_GRPC_ADAPTER,
39
40
  TRANSPORT_TYPE_GRPC_RERE,
40
41
  TRANSPORT_TYPE_REST,
41
42
  )
@@ -48,6 +49,7 @@ from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
48
49
  from flwr.proto.fleet_pb2_grpc import ( # pylint: disable=E0611
49
50
  add_FleetServicer_to_server,
50
51
  )
52
+ from flwr.proto.grpcadapter_pb2_grpc import add_GrpcAdapterServicer_to_server
51
53
 
52
54
  from .client_manager import ClientManager
53
55
  from .history import History
@@ -55,6 +57,7 @@ from .server import Server, init_defaults, run_fl
55
57
  from .server_config import ServerConfig
56
58
  from .strategy import Strategy
57
59
  from .superlink.driver.driver_grpc import run_driver_api_grpc
60
+ from .superlink.fleet.grpc_adapter.grpc_adapter_servicer import GrpcAdapterServicer
58
61
  from .superlink.fleet.grpc_bidi.grpc_server import (
59
62
  generic_create_grpc_server,
60
63
  start_grpc_server,
@@ -200,15 +203,7 @@ def run_superlink() -> None:
200
203
  args = _parse_args_run_superlink().parse_args()
201
204
 
202
205
  # Parse IP address
203
- parsed_driver_address = parse_address(args.driver_api_address)
204
- if not parsed_driver_address:
205
- sys.exit(f"Driver IP address ({args.driver_api_address}) cannot be parsed.")
206
- driver_host, driver_port, driver_is_v6 = parsed_driver_address
207
- driver_address = (
208
- f"[{driver_host}]:{driver_port}"
209
- if driver_is_v6
210
- else f"{driver_host}:{driver_port}"
211
- )
206
+ driver_address, _, _ = _format_address(args.driver_api_address)
212
207
 
213
208
  # Obtain certificates
214
209
  certificates = _try_obtain_certificates(args)
@@ -226,18 +221,15 @@ def run_superlink() -> None:
226
221
  grpc_servers = [driver_server]
227
222
  bckg_threads = []
228
223
  if not args.fleet_api_address:
229
- args.fleet_api_address = (
230
- ADDRESS_FLEET_API_GRPC_RERE
231
- if args.fleet_api_type == TRANSPORT_TYPE_GRPC_RERE
232
- else ADDRESS_FLEET_API_REST
233
- )
234
- parsed_fleet_address = parse_address(args.fleet_api_address)
235
- if not parsed_fleet_address:
236
- sys.exit(f"Fleet IP address ({args.fleet_api_address}) cannot be parsed.")
237
- fleet_host, fleet_port, fleet_is_v6 = parsed_fleet_address
238
- fleet_address = (
239
- f"[{fleet_host}]:{fleet_port}" if fleet_is_v6 else f"{fleet_host}:{fleet_port}"
240
- )
224
+ if args.fleet_api_type in [
225
+ TRANSPORT_TYPE_GRPC_RERE,
226
+ TRANSPORT_TYPE_GRPC_ADAPTER,
227
+ ]:
228
+ args.fleet_api_address = ADDRESS_FLEET_API_GRPC_RERE
229
+ elif args.fleet_api_type == TRANSPORT_TYPE_REST:
230
+ args.fleet_api_address = ADDRESS_FLEET_API_REST
231
+
232
+ fleet_address, host, port = _format_address(args.fleet_api_address)
241
233
 
242
234
  num_workers = args.fleet_api_num_workers
243
235
  if num_workers != 1:
@@ -267,8 +259,8 @@ def run_superlink() -> None:
267
259
  fleet_thread = threading.Thread(
268
260
  target=_run_fleet_api_rest,
269
261
  args=(
270
- fleet_host,
271
- fleet_port,
262
+ host,
263
+ port,
272
264
  ssl_keyfile,
273
265
  ssl_certfile,
274
266
  state_factory,
@@ -306,6 +298,13 @@ def run_superlink() -> None:
306
298
  interceptors=interceptors,
307
299
  )
308
300
  grpc_servers.append(fleet_server)
301
+ elif args.fleet_api_type == TRANSPORT_TYPE_GRPC_ADAPTER:
302
+ fleet_server = _run_fleet_api_grpc_adapter(
303
+ address=fleet_address,
304
+ state_factory=state_factory,
305
+ certificates=certificates,
306
+ )
307
+ grpc_servers.append(fleet_server)
309
308
  else:
310
309
  raise ValueError(f"Unknown fleet_api_type: {args.fleet_api_type}")
311
310
 
@@ -325,6 +324,16 @@ def run_superlink() -> None:
325
324
  driver_server.wait_for_termination(timeout=1)
326
325
 
327
326
 
327
+ def _format_address(address: str) -> Tuple[str, str, int]:
328
+ parsed_address = parse_address(address)
329
+ if not parsed_address:
330
+ sys.exit(
331
+ f"Address ({address}) cannot be parsed (expected: URL or IPv4 or IPv6)."
332
+ )
333
+ host, port, is_v6 = parsed_address
334
+ return (f"[{host}]:{port}" if is_v6 else f"{host}:{port}", host, port)
335
+
336
+
328
337
  def _try_setup_client_authentication(
329
338
  args: argparse.Namespace,
330
339
  certificates: Optional[Tuple[bytes, bytes, bytes]],
@@ -422,7 +431,7 @@ def _try_obtain_certificates(
422
431
  log(WARN, "Option `--insecure` was set. Starting insecure HTTP server.")
423
432
  return None
424
433
  # Check if certificates are provided
425
- if args.fleet_api_type == TRANSPORT_TYPE_GRPC_RERE:
434
+ if args.fleet_api_type in [TRANSPORT_TYPE_GRPC_RERE, TRANSPORT_TYPE_GRPC_ADAPTER]:
426
435
  if args.ssl_certfile and args.ssl_keyfile and args.ssl_ca_certfile:
427
436
  if not isfile(args.ssl_ca_certfile):
428
437
  sys.exit("Path argument `--ssl-ca-certfile` does not point to a file.")
@@ -494,6 +503,30 @@ def _run_fleet_api_grpc_rere(
494
503
  return fleet_grpc_server
495
504
 
496
505
 
506
+ def _run_fleet_api_grpc_adapter(
507
+ address: str,
508
+ state_factory: StateFactory,
509
+ certificates: Optional[Tuple[bytes, bytes, bytes]],
510
+ ) -> grpc.Server:
511
+ """Run Fleet API (GrpcAdapter)."""
512
+ # Create Fleet API gRPC server
513
+ fleet_servicer = GrpcAdapterServicer(
514
+ state_factory=state_factory,
515
+ )
516
+ fleet_add_servicer_to_server_fn = add_GrpcAdapterServicer_to_server
517
+ fleet_grpc_server = generic_create_grpc_server(
518
+ servicer_and_add_fn=(fleet_servicer, fleet_add_servicer_to_server_fn),
519
+ server_address=address,
520
+ max_message_length=GRPC_MAX_MESSAGE_LENGTH,
521
+ certificates=certificates,
522
+ )
523
+
524
+ log(INFO, "Flower ECE: Starting Fleet API (GrpcAdapter) on %s", address)
525
+ fleet_grpc_server.start()
526
+
527
+ return fleet_grpc_server
528
+
529
+
497
530
  # pylint: disable=import-outside-toplevel,too-many-arguments
498
531
  def _run_fleet_api_rest(
499
532
  host: str,
@@ -609,7 +642,11 @@ def _add_args_fleet_api(parser: argparse.ArgumentParser) -> None:
609
642
  "--fleet-api-type",
610
643
  default=TRANSPORT_TYPE_GRPC_RERE,
611
644
  type=str,
612
- choices=[TRANSPORT_TYPE_GRPC_RERE, TRANSPORT_TYPE_REST],
645
+ choices=[
646
+ TRANSPORT_TYPE_GRPC_RERE,
647
+ TRANSPORT_TYPE_GRPC_ADAPTER,
648
+ TRANSPORT_TYPE_REST,
649
+ ],
613
650
  help="Start a gRPC-rere or REST (experimental) Fleet API server.",
614
651
  )
615
652
  parser.add_argument(
@@ -45,12 +45,14 @@ def run(
45
45
  )
46
46
 
47
47
  if server_app_dir is not None:
48
- sys.path.insert(0, server_app_dir)
48
+ sys.path.insert(0, str(Path(server_app_dir).absolute()))
49
49
 
50
50
  # Load ServerApp if needed
51
51
  def _load() -> ServerApp:
52
52
  if server_app_attr:
53
- server_app: ServerApp = load_app(server_app_attr, LoadServerAppError)
53
+ server_app: ServerApp = load_app(
54
+ server_app_attr, LoadServerAppError, server_app_dir
55
+ )
54
56
 
55
57
  if not isinstance(server_app, ServerApp):
56
58
  raise LoadServerAppError(
@@ -53,9 +53,10 @@ __all__ = [
53
53
  "DPFedAvgAdaptive",
54
54
  "DPFedAvgFixed",
55
55
  "DifferentialPrivacyClientSideAdaptiveClipping",
56
- "DifferentialPrivacyServerSideAdaptiveClipping",
57
56
  "DifferentialPrivacyClientSideFixedClipping",
57
+ "DifferentialPrivacyServerSideAdaptiveClipping",
58
58
  "DifferentialPrivacyServerSideFixedClipping",
59
+ "FaultTolerantFedAvg",
59
60
  "FedAdagrad",
60
61
  "FedAdam",
61
62
  "FedAvg",
@@ -69,7 +70,6 @@ __all__ = [
69
70
  "FedXgbCyclic",
70
71
  "FedXgbNnAvg",
71
72
  "FedYogi",
72
- "FaultTolerantFedAvg",
73
73
  "Krum",
74
74
  "QFedAvg",
75
75
  "Strategy",
@@ -0,0 +1,15 @@
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Server-side part of the GrpcAdapter transport layer."""