flwr 1.19.0__py3-none-any.whl → 1.20.0__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.
- flwr/cli/build.py +15 -5
- flwr/cli/new/new.py +12 -4
- flwr/cli/new/templates/app/README.flowertune.md.tpl +2 -0
- flwr/cli/new/templates/app/README.md.tpl +5 -0
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +14 -3
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +13 -1
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +21 -2
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +18 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +19 -2
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +18 -1
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +20 -3
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +18 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +18 -1
- flwr/cli/run/run.py +45 -38
- flwr/cli/utils.py +12 -5
- flwr/client/grpc_adapter_client/connection.py +11 -4
- flwr/client/grpc_rere_client/connection.py +92 -117
- flwr/client/rest_client/connection.py +131 -164
- flwr/common/constant.py +3 -1
- flwr/common/exit/exit_code.py +16 -1
- flwr/common/grpc.py +12 -1
- flwr/common/{inflatable_grpc_utils.py → inflatable_protobuf_utils.py} +52 -10
- flwr/common/inflatable_utils.py +191 -24
- flwr/common/record/array.py +101 -22
- flwr/common/record/arraychunk.py +59 -0
- flwr/common/serde.py +0 -28
- flwr/compat/client/app.py +14 -31
- flwr/proto/appio_pb2.py +43 -0
- flwr/proto/appio_pb2.pyi +151 -0
- flwr/proto/appio_pb2_grpc.py +4 -0
- flwr/proto/appio_pb2_grpc.pyi +4 -0
- flwr/proto/clientappio_pb2.py +12 -19
- flwr/proto/clientappio_pb2.pyi +23 -101
- flwr/proto/clientappio_pb2_grpc.py +269 -28
- flwr/proto/clientappio_pb2_grpc.pyi +114 -20
- flwr/proto/fleet_pb2.py +12 -20
- flwr/proto/fleet_pb2.pyi +6 -36
- flwr/proto/serverappio_pb2.py +8 -31
- flwr/proto/serverappio_pb2.pyi +0 -152
- flwr/proto/serverappio_pb2_grpc.py +39 -38
- flwr/proto/serverappio_pb2_grpc.pyi +21 -20
- flwr/server/app.py +1 -1
- flwr/server/fleet_event_log_interceptor.py +4 -0
- flwr/server/grid/grpc_grid.py +91 -54
- flwr/server/serverapp/app.py +27 -17
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +8 -0
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +1 -1
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +2 -5
- flwr/server/superlink/fleet/message_handler/message_handler.py +10 -16
- flwr/server/superlink/fleet/rest_rere/rest_api.py +1 -2
- flwr/server/superlink/serverappio/serverappio_grpc.py +1 -1
- flwr/server/superlink/serverappio/serverappio_servicer.py +35 -43
- flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
- flwr/server/superlink/simulation/simulationio_servicer.py +1 -1
- flwr/server/superlink/utils.py +0 -35
- flwr/simulation/app.py +8 -0
- flwr/simulation/run_simulation.py +17 -0
- flwr/{server/superlink → supercore}/ffs/disk_ffs.py +1 -1
- flwr/supercore/grpc_health/__init__.py +22 -0
- flwr/supercore/grpc_health/simple_health_servicer.py +38 -0
- flwr/supercore/license_plugin/__init__.py +22 -0
- flwr/supercore/license_plugin/license_plugin.py +26 -0
- flwr/supercore/object_store/in_memory_object_store.py +31 -31
- flwr/supercore/object_store/object_store.py +20 -42
- flwr/supercore/object_store/utils.py +43 -0
- flwr/supercore/scheduler/__init__.py +22 -0
- flwr/supercore/scheduler/plugin.py +71 -0
- flwr/supercore/utils.py +32 -0
- flwr/superexec/deployment.py +1 -2
- flwr/superexec/exec_event_log_interceptor.py +4 -0
- flwr/superexec/exec_grpc.py +18 -2
- flwr/superexec/exec_license_interceptor.py +82 -0
- flwr/superexec/exec_servicer.py +10 -1
- flwr/superexec/exec_user_auth_interceptor.py +10 -2
- flwr/superexec/executor.py +1 -1
- flwr/superexec/simulation.py +1 -2
- flwr/supernode/cli/flower_supernode.py +0 -7
- flwr/supernode/cli/flwr_clientapp.py +10 -3
- flwr/supernode/nodestate/in_memory_nodestate.py +11 -2
- flwr/supernode/nodestate/nodestate.py +15 -0
- flwr/supernode/runtime/run_clientapp.py +110 -33
- flwr/supernode/scheduler/__init__.py +22 -0
- flwr/supernode/scheduler/simple_clientapp_scheduler_plugin.py +49 -0
- flwr/supernode/servicer/clientappio/__init__.py +1 -3
- flwr/supernode/servicer/clientappio/clientappio_servicer.py +223 -164
- flwr/supernode/start_client_internal.py +202 -104
- {flwr-1.19.0.dist-info → flwr-1.20.0.dist-info}/METADATA +2 -1
- {flwr-1.19.0.dist-info → flwr-1.20.0.dist-info}/RECORD +93 -78
- flwr/common/inflatable_rest_utils.py +0 -99
- /flwr/{server/superlink → supercore}/ffs/__init__.py +0 -0
- /flwr/{server/superlink → supercore}/ffs/ffs.py +0 -0
- /flwr/{server/superlink → supercore}/ffs/ffs_factory.py +0 -0
- {flwr-1.19.0.dist-info → flwr-1.20.0.dist-info}/WHEEL +0 -0
- {flwr-1.19.0.dist-info → flwr-1.20.0.dist-info}/entry_points.txt +0 -0
|
@@ -15,16 +15,15 @@
|
|
|
15
15
|
"""ClientAppIo API servicer."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
from dataclasses import dataclass
|
|
19
18
|
from logging import DEBUG, ERROR
|
|
20
|
-
from typing import
|
|
19
|
+
from typing import cast
|
|
21
20
|
|
|
22
21
|
import grpc
|
|
23
22
|
|
|
24
|
-
from flwr.common import Context
|
|
23
|
+
from flwr.common import Context
|
|
24
|
+
from flwr.common.inflatable import UnexpectedObjectContentError
|
|
25
25
|
from flwr.common.logger import log
|
|
26
26
|
from flwr.common.serde import (
|
|
27
|
-
clientappstatus_to_proto,
|
|
28
27
|
context_from_proto,
|
|
29
28
|
context_to_proto,
|
|
30
29
|
fab_to_proto,
|
|
@@ -36,209 +35,269 @@ from flwr.common.typing import Fab, Run
|
|
|
36
35
|
|
|
37
36
|
# pylint: disable=E0611
|
|
38
37
|
from flwr.proto import clientappio_pb2_grpc
|
|
38
|
+
from flwr.proto.appio_pb2 import ( # pylint: disable=E0401
|
|
39
|
+
PullAppInputsRequest,
|
|
40
|
+
PullAppInputsResponse,
|
|
41
|
+
PullAppMessagesRequest,
|
|
42
|
+
PullAppMessagesResponse,
|
|
43
|
+
PushAppMessagesRequest,
|
|
44
|
+
PushAppMessagesResponse,
|
|
45
|
+
PushAppOutputsRequest,
|
|
46
|
+
PushAppOutputsResponse,
|
|
47
|
+
)
|
|
39
48
|
from flwr.proto.clientappio_pb2 import ( # pylint: disable=E0401
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
GetRunIdsWithPendingMessagesRequest,
|
|
50
|
+
GetRunIdsWithPendingMessagesResponse,
|
|
51
|
+
RequestTokenRequest,
|
|
52
|
+
RequestTokenResponse,
|
|
53
|
+
)
|
|
54
|
+
from flwr.proto.message_pb2 import (
|
|
55
|
+
ConfirmMessageReceivedRequest,
|
|
56
|
+
ConfirmMessageReceivedResponse,
|
|
57
|
+
PullObjectRequest,
|
|
58
|
+
PullObjectResponse,
|
|
59
|
+
PushObjectRequest,
|
|
60
|
+
PushObjectResponse,
|
|
46
61
|
)
|
|
62
|
+
from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
|
|
47
63
|
|
|
64
|
+
# pylint: disable=E0601
|
|
65
|
+
from flwr.supercore.ffs import FfsFactory
|
|
66
|
+
from flwr.supercore.object_store import NoObjectInStoreError, ObjectStoreFactory
|
|
67
|
+
from flwr.supercore.object_store.utils import store_mapping_and_register_objects
|
|
68
|
+
from flwr.supernode.nodestate import NodeStateFactory
|
|
48
69
|
|
|
49
|
-
@dataclass
|
|
50
|
-
class ClientAppInputs:
|
|
51
|
-
"""Specify the inputs to the ClientApp."""
|
|
52
70
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
fab: Optional[Fab]
|
|
57
|
-
token: int
|
|
71
|
+
# pylint: disable=C0103,W0613,W0201
|
|
72
|
+
class ClientAppIoServicer(clientappio_pb2_grpc.ClientAppIoServicer):
|
|
73
|
+
"""ClientAppIo API servicer."""
|
|
58
74
|
|
|
75
|
+
def __init__(
|
|
76
|
+
self,
|
|
77
|
+
state_factory: NodeStateFactory,
|
|
78
|
+
ffs_factory: FfsFactory,
|
|
79
|
+
objectstore_factory: ObjectStoreFactory,
|
|
80
|
+
) -> None:
|
|
81
|
+
self.state_factory = state_factory
|
|
82
|
+
self.ffs_factory = ffs_factory
|
|
83
|
+
self.objectstore_factory = objectstore_factory
|
|
59
84
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
85
|
+
def GetRunIdsWithPendingMessages(
|
|
86
|
+
self,
|
|
87
|
+
request: GetRunIdsWithPendingMessagesRequest,
|
|
88
|
+
context: grpc.ServicerContext,
|
|
89
|
+
) -> GetRunIdsWithPendingMessagesResponse:
|
|
90
|
+
"""Get run IDs with pending messages."""
|
|
91
|
+
log(DEBUG, "ClientAppIo.GetRunIdsWithPendingMessages")
|
|
63
92
|
|
|
64
|
-
|
|
65
|
-
|
|
93
|
+
# Initialize state connection
|
|
94
|
+
state = self.state_factory.state()
|
|
66
95
|
|
|
96
|
+
# Get run IDs with pending messages
|
|
97
|
+
run_ids = state.get_run_ids_with_pending_messages()
|
|
67
98
|
|
|
68
|
-
#
|
|
69
|
-
|
|
70
|
-
"""ClientAppIo API servicer."""
|
|
99
|
+
# Return run IDs
|
|
100
|
+
return GetRunIdsWithPendingMessagesResponse(run_ids=run_ids)
|
|
71
101
|
|
|
72
|
-
def
|
|
73
|
-
self
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
102
|
+
def RequestToken(
|
|
103
|
+
self, request: RequestTokenRequest, context: grpc.ServicerContext
|
|
104
|
+
) -> RequestTokenResponse:
|
|
105
|
+
"""Request token."""
|
|
106
|
+
log(DEBUG, "ClientAppIo.RequestToken")
|
|
77
107
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
) -> GetTokenResponse:
|
|
81
|
-
"""Get token."""
|
|
82
|
-
log(DEBUG, "ClientAppIo.GetToken")
|
|
108
|
+
# Initialize state connection
|
|
109
|
+
state = self.state_factory.state()
|
|
83
110
|
|
|
84
|
-
#
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
111
|
+
# Attempt to create a token for the provided run ID
|
|
112
|
+
try:
|
|
113
|
+
token = state.create_token(request.run_id)
|
|
114
|
+
except ValueError:
|
|
115
|
+
# Return an empty token if A token already exists for this run ID,
|
|
116
|
+
# indicating the run is in progress
|
|
117
|
+
return RequestTokenResponse(token="")
|
|
91
118
|
|
|
92
|
-
#
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
119
|
+
# Return the token
|
|
120
|
+
return RequestTokenResponse(token=token)
|
|
121
|
+
|
|
122
|
+
def GetRun(
|
|
123
|
+
self, request: GetRunRequest, context: grpc.ServicerContext
|
|
124
|
+
) -> GetRunResponse:
|
|
125
|
+
"""Get run information."""
|
|
126
|
+
log(DEBUG, "ClientAppIo.GetRun")
|
|
98
127
|
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
#
|
|
103
|
-
|
|
104
|
-
|
|
128
|
+
# Initialize state connection
|
|
129
|
+
state = self.state_factory.state()
|
|
130
|
+
|
|
131
|
+
# Retrieve run information
|
|
132
|
+
run = state.get_run(request.run_id)
|
|
133
|
+
|
|
134
|
+
if run is None:
|
|
135
|
+
return GetRunResponse()
|
|
136
|
+
|
|
137
|
+
return GetRunResponse(run=run_to_proto(run))
|
|
105
138
|
|
|
106
139
|
def PullClientAppInputs(
|
|
107
|
-
self, request:
|
|
108
|
-
) ->
|
|
140
|
+
self, request: PullAppInputsRequest, context: grpc.ServicerContext
|
|
141
|
+
) -> PullAppInputsResponse:
|
|
109
142
|
"""Pull Message, Context, and Run."""
|
|
110
143
|
log(DEBUG, "ClientAppIo.PullClientAppInputs")
|
|
111
144
|
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
grpc.StatusCode.FAILED_PRECONDITION,
|
|
116
|
-
"No inputs available.",
|
|
117
|
-
)
|
|
118
|
-
clientapp_input = cast(ClientAppInputs, self.clientapp_input)
|
|
145
|
+
# Initialize state and ffs connection
|
|
146
|
+
state = self.state_factory.state()
|
|
147
|
+
ffs = self.ffs_factory.ffs()
|
|
119
148
|
|
|
120
|
-
#
|
|
121
|
-
|
|
149
|
+
# Validate the token
|
|
150
|
+
run_id = state.get_run_id_by_token(request.token)
|
|
151
|
+
if run_id is None or not state.verify_token(run_id, request.token):
|
|
122
152
|
context.abort(
|
|
123
|
-
grpc.StatusCode.
|
|
124
|
-
"
|
|
125
|
-
"Token must be returned before can be returned only once.",
|
|
153
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
|
154
|
+
"Invalid token.",
|
|
126
155
|
)
|
|
156
|
+
raise RuntimeError("This line should never be reached.")
|
|
127
157
|
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
"Mismatch between ClientApp and SuperNode token",
|
|
133
|
-
)
|
|
158
|
+
# Retrieve context, run and fab for this run
|
|
159
|
+
context = cast(Context, state.get_context(run_id))
|
|
160
|
+
run = cast(Run, state.get_run(run_id))
|
|
161
|
+
fab = Fab(run.fab_hash, ffs.get(run.fab_hash)[0]) # type: ignore
|
|
134
162
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
context=context_to_proto(clientapp_input.context),
|
|
140
|
-
run=run_to_proto(clientapp_input.run),
|
|
141
|
-
fab=fab_to_proto(clientapp_input.fab) if clientapp_input.fab else None,
|
|
163
|
+
return PullAppInputsResponse(
|
|
164
|
+
context=context_to_proto(context),
|
|
165
|
+
run=run_to_proto(run),
|
|
166
|
+
fab=fab_to_proto(fab),
|
|
142
167
|
)
|
|
143
168
|
|
|
144
169
|
def PushClientAppOutputs(
|
|
145
|
-
self, request:
|
|
146
|
-
) ->
|
|
170
|
+
self, request: PushAppOutputsRequest, context: grpc.ServicerContext
|
|
171
|
+
) -> PushAppOutputsResponse:
|
|
147
172
|
"""Push Message and Context."""
|
|
148
173
|
log(DEBUG, "ClientAppIo.PushClientAppOutputs")
|
|
149
174
|
|
|
150
|
-
#
|
|
151
|
-
|
|
152
|
-
context.abort(
|
|
153
|
-
grpc.StatusCode.FAILED_PRECONDITION,
|
|
154
|
-
"No inputs available.",
|
|
155
|
-
)
|
|
156
|
-
clientapp_input = cast(ClientAppInputs, self.clientapp_input)
|
|
175
|
+
# Initialize state connection
|
|
176
|
+
state = self.state_factory.state()
|
|
157
177
|
|
|
158
|
-
#
|
|
159
|
-
|
|
178
|
+
# Validate the token
|
|
179
|
+
run_id = state.get_run_id_by_token(request.token)
|
|
180
|
+
if run_id is None or not state.verify_token(run_id, request.token):
|
|
160
181
|
context.abort(
|
|
161
|
-
grpc.StatusCode.
|
|
162
|
-
"
|
|
163
|
-
"Token must be returned before can be returned only once.",
|
|
182
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
|
183
|
+
"Invalid token.",
|
|
164
184
|
)
|
|
185
|
+
raise RuntimeError("This line should never be reached.")
|
|
186
|
+
|
|
187
|
+
# Save the context to the state
|
|
188
|
+
state.store_context(context_from_proto(request.context))
|
|
165
189
|
|
|
166
|
-
#
|
|
167
|
-
|
|
190
|
+
# Remove the token to make the run eligible for processing
|
|
191
|
+
# A run associated with a token cannot be handled until its token is cleared
|
|
192
|
+
state.delete_token(run_id)
|
|
193
|
+
|
|
194
|
+
return PushAppOutputsResponse()
|
|
195
|
+
|
|
196
|
+
def PullMessage(
|
|
197
|
+
self, request: PullAppMessagesRequest, context: grpc.ServicerContext
|
|
198
|
+
) -> PullAppMessagesResponse:
|
|
199
|
+
"""Pull one Message."""
|
|
200
|
+
# Initialize state and store connection
|
|
201
|
+
state = self.state_factory.state()
|
|
202
|
+
store = self.objectstore_factory.store()
|
|
203
|
+
|
|
204
|
+
# Validate the token
|
|
205
|
+
run_id = state.get_run_id_by_token(request.token)
|
|
206
|
+
if run_id is None or not state.verify_token(run_id, request.token):
|
|
168
207
|
context.abort(
|
|
169
|
-
grpc.StatusCode.
|
|
170
|
-
"
|
|
171
|
-
"Inputs must be delivered before can be returned only once.",
|
|
208
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
|
209
|
+
"Invalid token.",
|
|
172
210
|
)
|
|
211
|
+
raise RuntimeError("This line should never be reached.")
|
|
212
|
+
|
|
213
|
+
# Retrieve message for this run
|
|
214
|
+
message = state.get_messages(run_ids=[run_id], is_reply=False)[0]
|
|
215
|
+
|
|
216
|
+
# Retrieve the object tree for the message
|
|
217
|
+
object_tree = store.get_object_tree(message.metadata.message_id)
|
|
173
218
|
|
|
174
|
-
|
|
175
|
-
|
|
219
|
+
return PullAppMessagesResponse(
|
|
220
|
+
messages_list=[message_to_proto(message)],
|
|
221
|
+
message_object_trees=[object_tree],
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
def PushMessage(
|
|
225
|
+
self, request: PushAppMessagesRequest, context: grpc.ServicerContext
|
|
226
|
+
) -> PushAppMessagesResponse:
|
|
227
|
+
"""Push one Message."""
|
|
228
|
+
# Initialize state and store connection
|
|
229
|
+
state = self.state_factory.state()
|
|
230
|
+
store = self.objectstore_factory.store()
|
|
231
|
+
|
|
232
|
+
# Validate the token
|
|
233
|
+
run_id = state.get_run_id_by_token(request.token)
|
|
234
|
+
if run_id is None or not state.verify_token(run_id, request.token):
|
|
176
235
|
context.abort(
|
|
177
|
-
grpc.StatusCode.
|
|
178
|
-
"
|
|
236
|
+
grpc.StatusCode.PERMISSION_DENIED,
|
|
237
|
+
"Invalid token.",
|
|
179
238
|
)
|
|
239
|
+
raise RuntimeError("This line should never be reached.")
|
|
240
|
+
|
|
241
|
+
# Save the message to the state
|
|
242
|
+
state.store_message(message_from_proto(request.messages_list[0]))
|
|
243
|
+
|
|
244
|
+
# Store Message object to descendants mapping and preregister objects
|
|
245
|
+
objects_to_push = store_mapping_and_register_objects(store, request=request)
|
|
246
|
+
|
|
247
|
+
return PushAppMessagesResponse(objects_to_push=objects_to_push)
|
|
180
248
|
|
|
181
|
-
|
|
249
|
+
def PushObject(
|
|
250
|
+
self, request: PushObjectRequest, context: grpc.ServicerContext
|
|
251
|
+
) -> PushObjectResponse:
|
|
252
|
+
"""Push an object to the ObjectStore."""
|
|
253
|
+
log(DEBUG, "ServerAppIoServicer.PushObject")
|
|
254
|
+
|
|
255
|
+
# Init state and store
|
|
256
|
+
store = self.objectstore_factory.store()
|
|
257
|
+
|
|
258
|
+
# Insert in store
|
|
259
|
+
stored = False
|
|
182
260
|
try:
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
261
|
+
store.put(request.object_id, request.object_content)
|
|
262
|
+
stored = True
|
|
263
|
+
except (NoObjectInStoreError, ValueError) as e:
|
|
264
|
+
log(ERROR, str(e))
|
|
265
|
+
except UnexpectedObjectContentError as e:
|
|
266
|
+
# Object content is not valid
|
|
267
|
+
context.abort(grpc.StatusCode.FAILED_PRECONDITION, str(e))
|
|
268
|
+
|
|
269
|
+
return PushObjectResponse(stored=stored)
|
|
270
|
+
|
|
271
|
+
def PullObject(
|
|
272
|
+
self, request: PullObjectRequest, context: grpc.ServicerContext
|
|
273
|
+
) -> PullObjectResponse:
|
|
274
|
+
"""Pull an object from the ObjectStore."""
|
|
275
|
+
log(DEBUG, "ServerAppIoServicer.PullObject")
|
|
276
|
+
|
|
277
|
+
# Init state and store
|
|
278
|
+
store = self.objectstore_factory.store()
|
|
279
|
+
|
|
280
|
+
# Fetch from store
|
|
281
|
+
content = store.get(request.object_id)
|
|
282
|
+
if content is not None:
|
|
283
|
+
object_available = content != b""
|
|
284
|
+
return PullObjectResponse(
|
|
285
|
+
object_found=True,
|
|
286
|
+
object_available=object_available,
|
|
287
|
+
object_content=content,
|
|
187
288
|
)
|
|
289
|
+
return PullObjectResponse(object_found=False, object_available=False)
|
|
188
290
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
code = typing.ClientAppOutputCode.UNKNOWN_ERROR
|
|
195
|
-
status = typing.ClientAppOutputStatus(code=code, message="Unkonwn error")
|
|
291
|
+
def ConfirmMessageReceived(
|
|
292
|
+
self, request: ConfirmMessageReceivedRequest, context: grpc.ServicerContext
|
|
293
|
+
) -> ConfirmMessageReceivedResponse:
|
|
294
|
+
"""Confirm message received."""
|
|
295
|
+
log(DEBUG, "ServerAppIoServicer.ConfirmMessageReceived")
|
|
196
296
|
|
|
197
|
-
#
|
|
198
|
-
|
|
199
|
-
return PushClientAppOutputsResponse(status=proto_status)
|
|
297
|
+
# Init state and store
|
|
298
|
+
store = self.objectstore_factory.store()
|
|
200
299
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
Parameters
|
|
207
|
-
----------
|
|
208
|
-
clientapp_input : ClientAppInputs
|
|
209
|
-
The inputs to the ClientApp.
|
|
210
|
-
token_returned : bool
|
|
211
|
-
A boolean indicating if the token has been returned.
|
|
212
|
-
Set to `True` when passing the token to `flwr-clientap`
|
|
213
|
-
and `False` otherwise.
|
|
214
|
-
"""
|
|
215
|
-
if (
|
|
216
|
-
self.clientapp_input is not None
|
|
217
|
-
or self.clientapp_output is not None
|
|
218
|
-
or self.token_returned
|
|
219
|
-
):
|
|
220
|
-
raise ValueError(
|
|
221
|
-
"ClientAppInputs and ClientAppOutputs must not be set before "
|
|
222
|
-
"calling `set_inputs`."
|
|
223
|
-
)
|
|
224
|
-
log(DEBUG, "ClientAppInputs set (token: %s)", clientapp_input.token)
|
|
225
|
-
self.clientapp_input = clientapp_input
|
|
226
|
-
self.token_returned = token_returned
|
|
227
|
-
|
|
228
|
-
def has_outputs(self) -> bool:
|
|
229
|
-
"""Check if ClientAppOutputs are available."""
|
|
230
|
-
return self.clientapp_output is not None
|
|
231
|
-
|
|
232
|
-
def get_outputs(self) -> ClientAppOutputs:
|
|
233
|
-
"""Get ClientApp outputs."""
|
|
234
|
-
if self.clientapp_output is None:
|
|
235
|
-
raise ValueError("ClientAppOutputs not set before calling `get_outputs`.")
|
|
236
|
-
|
|
237
|
-
# Set outputs to a local variable and clear state
|
|
238
|
-
output: ClientAppOutputs = self.clientapp_output
|
|
239
|
-
self.clientapp_input = None
|
|
240
|
-
self.clientapp_output = None
|
|
241
|
-
self.token_returned = False
|
|
242
|
-
self.inputs_returned = False
|
|
243
|
-
|
|
244
|
-
return output
|
|
300
|
+
# Delete the message object
|
|
301
|
+
store.delete(request.message_object_id)
|
|
302
|
+
|
|
303
|
+
return ConfirmMessageReceivedResponse()
|