flwr-nightly 1.23.0.dev20250930__py3-none-any.whl → 1.26.0.dev20260121__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.
Files changed (375) hide show
  1. flwr/__init__.py +17 -6
  2. flwr/app/__init__.py +4 -1
  3. flwr/app/error.py +2 -2
  4. flwr/app/exception.py +3 -3
  5. flwr/app/message_type.py +29 -0
  6. flwr/app/metadata.py +5 -2
  7. flwr/app/user_config.py +19 -0
  8. flwr/cli/app.py +62 -9
  9. flwr/cli/{new/templates/app/code → app_cmd}/__init__.py +9 -1
  10. flwr/cli/app_cmd/publish.py +285 -0
  11. flwr/cli/app_cmd/review.py +262 -0
  12. flwr/cli/auth_plugin/__init__.py +13 -6
  13. flwr/cli/auth_plugin/auth_plugin.py +26 -15
  14. flwr/cli/auth_plugin/noop_auth_plugin.py +101 -0
  15. flwr/cli/auth_plugin/oidc_cli_plugin.py +52 -32
  16. flwr/cli/build.py +166 -53
  17. flwr/cli/{cli_user_auth_interceptor.py → cli_account_auth_interceptor.py} +27 -10
  18. flwr/cli/config/__init__.py +21 -0
  19. flwr/cli/config/ls.py +104 -0
  20. flwr/cli/config_migration.py +300 -0
  21. flwr/cli/config_utils.py +154 -13
  22. flwr/cli/constant.py +67 -0
  23. flwr/cli/{new/templates/app/code/flwr_tune → federation}/__init__.py +8 -1
  24. flwr/cli/federation/ls.py +361 -0
  25. flwr/cli/flower_config.py +447 -0
  26. flwr/cli/install.py +91 -13
  27. flwr/cli/log.py +65 -36
  28. flwr/cli/login/login.py +41 -27
  29. flwr/cli/ls.py +232 -158
  30. flwr/cli/new/new.py +188 -244
  31. flwr/cli/pull.py +25 -34
  32. flwr/cli/run/run.py +106 -74
  33. flwr/cli/run_utils.py +148 -0
  34. flwr/cli/stop.py +46 -37
  35. flwr/cli/supernode/__init__.py +25 -0
  36. flwr/cli/supernode/ls.py +273 -0
  37. flwr/cli/supernode/register.py +190 -0
  38. flwr/cli/supernode/unregister.py +140 -0
  39. flwr/cli/typing.py +211 -0
  40. flwr/cli/utils.py +428 -80
  41. flwr/client/__init__.py +2 -1
  42. flwr/client/dpfedavg_numpy_client.py +4 -1
  43. flwr/client/grpc_adapter_client/connection.py +14 -17
  44. flwr/client/grpc_rere_client/connection.py +73 -43
  45. flwr/client/grpc_rere_client/grpc_adapter.py +35 -15
  46. flwr/client/grpc_rere_client/{client_interceptor.py → node_auth_client_interceptor.py} +5 -7
  47. flwr/client/message_handler/message_handler.py +4 -3
  48. flwr/client/mod/centraldp_mods.py +1 -1
  49. flwr/client/mod/localdp_mod.py +1 -1
  50. flwr/client/mod/secure_aggregation/secaggplus_mod.py +11 -9
  51. flwr/client/numpy_client.py +1 -1
  52. flwr/client/rest_client/connection.py +99 -54
  53. flwr/client/run_info_store.py +6 -6
  54. flwr/client/typing.py +1 -1
  55. flwr/clientapp/__init__.py +1 -2
  56. flwr/{client → clientapp}/client_app.py +11 -11
  57. flwr/clientapp/mod/centraldp_mods.py +16 -17
  58. flwr/clientapp/mod/localdp_mod.py +8 -9
  59. flwr/clientapp/typing.py +1 -1
  60. flwr/{client/clientapp → clientapp}/utils.py +4 -4
  61. flwr/common/__init__.py +3 -2
  62. flwr/common/args.py +3 -4
  63. flwr/common/config.py +15 -17
  64. flwr/common/constant.py +56 -28
  65. flwr/common/context.py +2 -1
  66. flwr/common/differential_privacy.py +3 -4
  67. flwr/common/event_log_plugin/event_log_plugin.py +3 -4
  68. flwr/common/exit/exit.py +16 -3
  69. flwr/common/exit/exit_code.py +39 -10
  70. flwr/common/exit/exit_handler.py +6 -2
  71. flwr/common/exit/signal_handler.py +5 -5
  72. flwr/common/grpc.py +8 -7
  73. flwr/common/inflatable_protobuf_utils.py +1 -1
  74. flwr/common/inflatable_utils.py +48 -31
  75. flwr/common/logger.py +19 -19
  76. flwr/common/message.py +5 -5
  77. flwr/common/object_ref.py +7 -7
  78. flwr/common/record/array.py +6 -6
  79. flwr/common/record/arrayrecord.py +18 -21
  80. flwr/common/record/configrecord.py +3 -3
  81. flwr/common/record/recorddict.py +5 -5
  82. flwr/common/record/typeddict.py +9 -2
  83. flwr/common/recorddict_compat.py +7 -10
  84. flwr/common/retry_invoker.py +20 -20
  85. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -89
  86. flwr/common/secure_aggregation/ndarrays_arithmetic.py +8 -5
  87. flwr/common/serde.py +22 -11
  88. flwr/common/serde_utils.py +2 -2
  89. flwr/common/telemetry.py +10 -6
  90. flwr/common/typing.py +65 -44
  91. flwr/compat/client/app.py +45 -47
  92. flwr/compat/client/grpc_client/connection.py +15 -14
  93. flwr/compat/common/constant.py +29 -0
  94. flwr/compat/server/app.py +6 -7
  95. flwr/proto/appio_pb2.py +13 -3
  96. flwr/proto/appio_pb2.pyi +134 -65
  97. flwr/proto/appio_pb2_grpc.py +20 -0
  98. flwr/proto/appio_pb2_grpc.pyi +27 -0
  99. flwr/proto/clientappio_pb2.py +17 -7
  100. flwr/proto/clientappio_pb2.pyi +15 -0
  101. flwr/proto/clientappio_pb2_grpc.py +206 -40
  102. flwr/proto/clientappio_pb2_grpc.pyi +168 -53
  103. flwr/proto/control_pb2.py +72 -40
  104. flwr/proto/control_pb2.pyi +319 -87
  105. flwr/proto/control_pb2_grpc.py +339 -28
  106. flwr/proto/control_pb2_grpc.pyi +209 -37
  107. flwr/proto/error_pb2.py +13 -3
  108. flwr/proto/error_pb2.pyi +24 -6
  109. flwr/proto/error_pb2_grpc.py +20 -0
  110. flwr/proto/error_pb2_grpc.pyi +27 -0
  111. flwr/proto/fab_pb2.py +24 -10
  112. flwr/proto/fab_pb2.pyi +68 -20
  113. flwr/proto/fab_pb2_grpc.py +20 -0
  114. flwr/proto/fab_pb2_grpc.pyi +27 -0
  115. flwr/proto/federation_pb2.py +38 -0
  116. flwr/proto/federation_pb2.pyi +56 -0
  117. flwr/proto/federation_pb2_grpc.py +24 -0
  118. flwr/proto/federation_pb2_grpc.pyi +31 -0
  119. flwr/proto/fleet_pb2.py +45 -27
  120. flwr/proto/fleet_pb2.pyi +190 -70
  121. flwr/proto/fleet_pb2_grpc.py +277 -66
  122. flwr/proto/fleet_pb2_grpc.pyi +201 -55
  123. flwr/proto/grpcadapter_pb2.py +14 -4
  124. flwr/proto/grpcadapter_pb2.pyi +38 -16
  125. flwr/proto/grpcadapter_pb2_grpc.py +35 -4
  126. flwr/proto/grpcadapter_pb2_grpc.pyi +38 -7
  127. flwr/proto/heartbeat_pb2.py +17 -7
  128. flwr/proto/heartbeat_pb2.pyi +51 -22
  129. flwr/proto/heartbeat_pb2_grpc.py +20 -0
  130. flwr/proto/heartbeat_pb2_grpc.pyi +27 -0
  131. flwr/proto/log_pb2.py +13 -3
  132. flwr/proto/log_pb2.pyi +34 -11
  133. flwr/proto/log_pb2_grpc.py +20 -0
  134. flwr/proto/log_pb2_grpc.pyi +27 -0
  135. flwr/proto/message_pb2.py +15 -5
  136. flwr/proto/message_pb2.pyi +154 -86
  137. flwr/proto/message_pb2_grpc.py +20 -0
  138. flwr/proto/message_pb2_grpc.pyi +27 -0
  139. flwr/proto/node_pb2.py +16 -4
  140. flwr/proto/node_pb2.pyi +77 -4
  141. flwr/proto/node_pb2_grpc.py +20 -0
  142. flwr/proto/node_pb2_grpc.pyi +27 -0
  143. flwr/proto/recorddict_pb2.py +13 -3
  144. flwr/proto/recorddict_pb2.pyi +184 -107
  145. flwr/proto/recorddict_pb2_grpc.py +20 -0
  146. flwr/proto/recorddict_pb2_grpc.pyi +27 -0
  147. flwr/proto/run_pb2.py +40 -31
  148. flwr/proto/run_pb2.pyi +158 -84
  149. flwr/proto/run_pb2_grpc.py +20 -0
  150. flwr/proto/run_pb2_grpc.pyi +27 -0
  151. flwr/proto/serverappio_pb2.py +13 -3
  152. flwr/proto/serverappio_pb2.pyi +32 -8
  153. flwr/proto/serverappio_pb2_grpc.py +246 -65
  154. flwr/proto/serverappio_pb2_grpc.pyi +221 -85
  155. flwr/proto/simulationio_pb2.py +16 -8
  156. flwr/proto/simulationio_pb2.pyi +15 -0
  157. flwr/proto/simulationio_pb2_grpc.py +162 -41
  158. flwr/proto/simulationio_pb2_grpc.pyi +149 -55
  159. flwr/proto/transport_pb2.py +20 -10
  160. flwr/proto/transport_pb2.pyi +249 -160
  161. flwr/proto/transport_pb2_grpc.py +35 -4
  162. flwr/proto/transport_pb2_grpc.pyi +38 -8
  163. flwr/server/app.py +175 -128
  164. flwr/server/client_manager.py +4 -5
  165. flwr/server/client_proxy.py +10 -11
  166. flwr/server/compat/app.py +4 -5
  167. flwr/server/compat/app_utils.py +2 -1
  168. flwr/server/compat/grid_client_proxy.py +12 -13
  169. flwr/server/compat/legacy_context.py +3 -4
  170. flwr/server/fleet_event_log_interceptor.py +2 -1
  171. flwr/server/grid/grid.py +2 -3
  172. flwr/server/grid/grpc_grid.py +12 -10
  173. flwr/server/grid/inmemory_grid.py +4 -4
  174. flwr/server/run_serverapp.py +2 -3
  175. flwr/server/server.py +34 -39
  176. flwr/server/server_app.py +7 -8
  177. flwr/server/server_config.py +1 -2
  178. flwr/server/serverapp/app.py +34 -28
  179. flwr/server/serverapp_components.py +4 -5
  180. flwr/server/strategy/aggregate.py +9 -8
  181. flwr/server/strategy/bulyan.py +13 -11
  182. flwr/server/strategy/dp_adaptive_clipping.py +16 -20
  183. flwr/server/strategy/dp_fixed_clipping.py +12 -17
  184. flwr/server/strategy/dpfedavg_adaptive.py +3 -4
  185. flwr/server/strategy/dpfedavg_fixed.py +6 -10
  186. flwr/server/strategy/fault_tolerant_fedavg.py +14 -13
  187. flwr/server/strategy/fedadagrad.py +18 -14
  188. flwr/server/strategy/fedadam.py +16 -14
  189. flwr/server/strategy/fedavg.py +16 -17
  190. flwr/server/strategy/fedavg_android.py +15 -15
  191. flwr/server/strategy/fedavgm.py +21 -18
  192. flwr/server/strategy/fedmedian.py +2 -3
  193. flwr/server/strategy/fedopt.py +11 -10
  194. flwr/server/strategy/fedprox.py +10 -9
  195. flwr/server/strategy/fedtrimmedavg.py +12 -11
  196. flwr/server/strategy/fedxgb_bagging.py +13 -11
  197. flwr/server/strategy/fedxgb_cyclic.py +6 -6
  198. flwr/server/strategy/fedxgb_nn_avg.py +4 -4
  199. flwr/server/strategy/fedyogi.py +16 -14
  200. flwr/server/strategy/krum.py +12 -11
  201. flwr/server/strategy/qfedavg.py +16 -15
  202. flwr/server/strategy/strategy.py +6 -9
  203. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +20 -9
  204. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -2
  205. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -4
  206. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +10 -12
  207. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +1 -3
  208. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +136 -42
  209. flwr/server/superlink/fleet/grpc_rere/{server_interceptor.py → node_auth_server_interceptor.py} +28 -50
  210. flwr/server/superlink/fleet/message_handler/message_handler.py +141 -51
  211. flwr/server/superlink/fleet/rest_rere/rest_api.py +54 -33
  212. flwr/server/superlink/fleet/vce/backend/backend.py +2 -2
  213. flwr/server/superlink/fleet/vce/backend/raybackend.py +6 -6
  214. flwr/server/superlink/fleet/vce/vce_api.py +32 -13
  215. flwr/server/superlink/linkstate/__init__.py +2 -0
  216. flwr/server/superlink/linkstate/in_memory_linkstate.py +293 -208
  217. flwr/server/superlink/linkstate/linkstate.py +176 -64
  218. flwr/server/superlink/linkstate/linkstate_factory.py +24 -6
  219. flwr/server/superlink/linkstate/sql_linkstate.py +221 -0
  220. flwr/server/superlink/linkstate/sqlite_linkstate.py +743 -648
  221. flwr/server/superlink/linkstate/utils.py +11 -62
  222. flwr/server/superlink/serverappio/serverappio_grpc.py +1 -2
  223. flwr/server/superlink/serverappio/serverappio_servicer.py +28 -23
  224. flwr/server/superlink/simulation/simulationio_grpc.py +1 -2
  225. flwr/server/superlink/simulation/simulationio_servicer.py +19 -14
  226. flwr/server/superlink/utils.py +4 -6
  227. flwr/server/typing.py +1 -1
  228. flwr/server/utils/tensorboard.py +15 -8
  229. flwr/server/utils/validator.py +2 -3
  230. flwr/server/workflow/default_workflows.py +7 -6
  231. flwr/server/workflow/secure_aggregation/secagg_workflow.py +2 -4
  232. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +13 -11
  233. flwr/serverapp/strategy/bulyan.py +16 -15
  234. flwr/serverapp/strategy/dp_adaptive_clipping.py +12 -11
  235. flwr/serverapp/strategy/dp_fixed_clipping.py +11 -14
  236. flwr/serverapp/strategy/fedadagrad.py +10 -11
  237. flwr/serverapp/strategy/fedadam.py +10 -11
  238. flwr/serverapp/strategy/fedavg.py +10 -11
  239. flwr/serverapp/strategy/fedavgm.py +17 -16
  240. flwr/serverapp/strategy/fedmedian.py +2 -2
  241. flwr/serverapp/strategy/fedopt.py +10 -11
  242. flwr/serverapp/strategy/fedprox.py +7 -8
  243. flwr/serverapp/strategy/fedtrimmedavg.py +9 -9
  244. flwr/serverapp/strategy/fedxgb_bagging.py +3 -3
  245. flwr/serverapp/strategy/fedxgb_cyclic.py +10 -10
  246. flwr/serverapp/strategy/fedyogi.py +9 -11
  247. flwr/serverapp/strategy/krum.py +7 -7
  248. flwr/serverapp/strategy/multikrum.py +9 -9
  249. flwr/serverapp/strategy/qfedavg.py +17 -16
  250. flwr/serverapp/strategy/strategy.py +6 -9
  251. flwr/serverapp/strategy/strategy_utils.py +7 -8
  252. flwr/simulation/app.py +46 -42
  253. flwr/simulation/legacy_app.py +12 -12
  254. flwr/simulation/ray_transport/ray_actor.py +11 -12
  255. flwr/simulation/ray_transport/ray_client_proxy.py +14 -19
  256. flwr/simulation/run_simulation.py +46 -44
  257. flwr/simulation/simulationio_connection.py +4 -4
  258. flwr/{common → supercore}/address.py +1 -37
  259. flwr/supercore/cli/flower_superexec.py +3 -4
  260. flwr/supercore/constant.py +69 -0
  261. flwr/supercore/corestate/corestate.py +24 -3
  262. flwr/supercore/corestate/in_memory_corestate.py +138 -0
  263. flwr/supercore/corestate/sql_corestate.py +153 -0
  264. flwr/supercore/corestate/sqlite_corestate.py +157 -0
  265. flwr/supercore/credential_store/__init__.py +33 -0
  266. flwr/supercore/credential_store/credential_store.py +34 -0
  267. flwr/supercore/credential_store/file_credential_store.py +76 -0
  268. flwr/{common → supercore}/date.py +0 -11
  269. flwr/supercore/ffs/disk_ffs.py +1 -2
  270. flwr/supercore/ffs/ffs.py +1 -2
  271. flwr/supercore/ffs/ffs_factory.py +1 -2
  272. flwr/{common → supercore}/heartbeat.py +20 -25
  273. flwr/supercore/object_store/in_memory_object_store.py +1 -6
  274. flwr/supercore/object_store/object_store.py +1 -2
  275. flwr/supercore/object_store/object_store_factory.py +27 -8
  276. flwr/supercore/object_store/sqlite_object_store.py +253 -0
  277. flwr/{cli/new/templates/app → supercore/primitives}/__init__.py +1 -1
  278. flwr/supercore/primitives/asymmetric.py +117 -0
  279. flwr/supercore/primitives/asymmetric_ed25519.py +175 -0
  280. flwr/supercore/sql_mixin.py +292 -0
  281. flwr/supercore/sqlite_mixin.py +156 -0
  282. flwr/{client/clientapp → supercore/state}/__init__.py +2 -2
  283. flwr/supercore/state/schema/README.md +125 -0
  284. flwr/{cli/new/templates → supercore/state/schema}/__init__.py +2 -2
  285. flwr/supercore/state/schema/corestate_tables.py +36 -0
  286. flwr/supercore/state/schema/linkstate_tables.py +152 -0
  287. flwr/supercore/state/schema/objectstore_tables.py +90 -0
  288. flwr/supercore/superexec/plugin/base_exec_plugin.py +1 -2
  289. flwr/supercore/superexec/plugin/exec_plugin.py +3 -3
  290. flwr/supercore/superexec/run_superexec.py +9 -13
  291. flwr/supercore/utils.py +224 -0
  292. flwr/superlink/artifact_provider/artifact_provider.py +1 -2
  293. flwr/superlink/auth_plugin/__init__.py +5 -2
  294. flwr/superlink/auth_plugin/auth_plugin.py +20 -19
  295. flwr/superlink/auth_plugin/noop_auth_plugin.py +84 -0
  296. flwr/superlink/federation/__init__.py +24 -0
  297. flwr/superlink/federation/federation_manager.py +64 -0
  298. flwr/superlink/federation/noop_federation_manager.py +71 -0
  299. flwr/superlink/servicer/control/{control_user_auth_interceptor.py → control_account_auth_interceptor.py} +41 -32
  300. flwr/superlink/servicer/control/control_event_log_interceptor.py +7 -7
  301. flwr/superlink/servicer/control/control_grpc.py +20 -17
  302. flwr/superlink/servicer/control/control_license_interceptor.py +3 -3
  303. flwr/superlink/servicer/control/control_servicer.py +328 -68
  304. flwr/supernode/cli/flower_supernode.py +74 -26
  305. flwr/supernode/nodestate/in_memory_nodestate.py +121 -49
  306. flwr/supernode/nodestate/nodestate.py +52 -8
  307. flwr/supernode/nodestate/nodestate_factory.py +7 -4
  308. flwr/supernode/runtime/run_clientapp.py +43 -24
  309. flwr/supernode/servicer/clientappio/clientappio_servicer.py +48 -10
  310. flwr/supernode/start_client_internal.py +185 -57
  311. {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/METADATA +10 -11
  312. flwr_nightly-1.26.0.dev20260121.dist-info/RECORD +411 -0
  313. flwr/cli/new/templates/app/.gitignore.tpl +0 -163
  314. flwr/cli/new/templates/app/LICENSE.tpl +0 -202
  315. flwr/cli/new/templates/app/README.baseline.md.tpl +0 -127
  316. flwr/cli/new/templates/app/README.flowertune.md.tpl +0 -68
  317. flwr/cli/new/templates/app/README.md.tpl +0 -37
  318. flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +0 -1
  319. flwr/cli/new/templates/app/code/__init__.py.tpl +0 -1
  320. flwr/cli/new/templates/app/code/__init__.pytorch_legacy_api.py.tpl +0 -1
  321. flwr/cli/new/templates/app/code/client.baseline.py.tpl +0 -75
  322. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +0 -93
  323. flwr/cli/new/templates/app/code/client.jax.py.tpl +0 -71
  324. flwr/cli/new/templates/app/code/client.mlx.py.tpl +0 -102
  325. flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -46
  326. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -80
  327. flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +0 -55
  328. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +0 -108
  329. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -82
  330. flwr/cli/new/templates/app/code/client.xgboost.py.tpl +0 -110
  331. flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +0 -36
  332. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +0 -92
  333. flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +0 -87
  334. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +0 -56
  335. flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +0 -73
  336. flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +0 -78
  337. flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -66
  338. flwr/cli/new/templates/app/code/server.baseline.py.tpl +0 -43
  339. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +0 -42
  340. flwr/cli/new/templates/app/code/server.jax.py.tpl +0 -39
  341. flwr/cli/new/templates/app/code/server.mlx.py.tpl +0 -41
  342. flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -38
  343. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -41
  344. flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +0 -31
  345. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +0 -44
  346. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -38
  347. flwr/cli/new/templates/app/code/server.xgboost.py.tpl +0 -56
  348. flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +0 -1
  349. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +0 -98
  350. flwr/cli/new/templates/app/code/task.jax.py.tpl +0 -57
  351. flwr/cli/new/templates/app/code/task.mlx.py.tpl +0 -102
  352. flwr/cli/new/templates/app/code/task.numpy.py.tpl +0 -7
  353. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -98
  354. flwr/cli/new/templates/app/code/task.pytorch_legacy_api.py.tpl +0 -111
  355. flwr/cli/new/templates/app/code/task.sklearn.py.tpl +0 -67
  356. flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +0 -52
  357. flwr/cli/new/templates/app/code/task.xgboost.py.tpl +0 -67
  358. flwr/cli/new/templates/app/code/utils.baseline.py.tpl +0 -1
  359. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +0 -146
  360. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +0 -80
  361. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +0 -65
  362. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +0 -52
  363. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +0 -56
  364. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +0 -49
  365. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +0 -53
  366. flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl +0 -53
  367. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +0 -52
  368. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +0 -53
  369. flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +0 -61
  370. flwr/common/pyproject.py +0 -42
  371. flwr/supercore/object_store/utils.py +0 -43
  372. flwr_nightly-1.23.0.dev20250930.dist-info/RECORD +0 -429
  373. /flwr/{common → supercore}/version.py +0 -0
  374. {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/WHEEL +0 -0
  375. {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,292 @@
1
+ # Copyright 2026 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
+ """Mixin providing common SQL connection and initialization logic via SQLAlchemy."""
16
+
17
+
18
+ import re
19
+ from abc import ABC
20
+ from collections.abc import Iterator, Sequence
21
+ from contextlib import contextmanager
22
+ from contextvars import ContextVar
23
+ from logging import DEBUG, ERROR
24
+ from pathlib import Path
25
+ from typing import Any
26
+
27
+ from sqlalchemy import Engine, MetaData, create_engine, event, inspect, text
28
+ from sqlalchemy.exc import SQLAlchemyError
29
+ from sqlalchemy.orm import Session, sessionmaker
30
+
31
+ from flwr.common.logger import log
32
+ from flwr.supercore.constant import SQLITE_PRAGMAS
33
+
34
+ _current_session: ContextVar[Session | None] = ContextVar(
35
+ "current_sqlalchemy_session",
36
+ default=None,
37
+ )
38
+
39
+
40
+ def _set_sqlite_pragmas(dbapi_conn: Any, _connection_record: Any) -> None:
41
+ """Set SQLite pragmas for performance and correctness."""
42
+ cursor = dbapi_conn.cursor()
43
+ for pragma, value in SQLITE_PRAGMAS:
44
+ cursor.execute(f"PRAGMA {pragma} = {value};")
45
+ cursor.close()
46
+
47
+
48
+ def _log_query( # pylint: disable=W0613,R0913,R0917
49
+ conn: Any,
50
+ cursor: Any,
51
+ statement: str,
52
+ parameters: Any,
53
+ context: Any,
54
+ executemany: bool,
55
+ ) -> None:
56
+ """Log SQL queries via Flower logger."""
57
+ log(DEBUG, {"query": statement, "params": parameters})
58
+
59
+
60
+ class SqlMixin(ABC):
61
+ """Mixin providing common SQLite connection and initialization logic.
62
+
63
+ This mixin uses SQLAlchemy Core API for SQLite database access. It accepts either a
64
+ database file path or a SQLite URL, automatically converting file paths to SQLite
65
+ URLs.
66
+ """
67
+
68
+ def __init__(self, database_path: str) -> None:
69
+ """Initialize the SqlMixin.
70
+
71
+ Parameters
72
+ ----------
73
+ database_path : str
74
+ Either a file path or SQLite database URL. Examples:
75
+ - "path/to/db.db" or "/absolute/path/to/db.db"
76
+ - ":memory:" for in-memory SQLite
77
+ - "sqlite:///path/to/db.db" for explicit SQLite URL
78
+ """
79
+ # Auto-convert file path to SQLAlchemy SQLite URL if needed
80
+ if database_path == ":memory:":
81
+ self.database_url = "sqlite:///:memory:"
82
+ elif not database_path.startswith("sqlite://"):
83
+ # Treat as file path, convert to absolute and create SQLite URL
84
+ abs_path = Path(database_path).resolve()
85
+ self.database_url = f"sqlite:///{abs_path}"
86
+ else:
87
+ # Already a SQLite URL
88
+ self.database_url = database_path
89
+
90
+ self._engine: Engine | None = None
91
+ self._session_factory: sessionmaker[Session] | None = None
92
+
93
+ @contextmanager
94
+ def session(self) -> Iterator[Session]:
95
+ """Provide a transactional database session context.
96
+
97
+ Yields a SQLAlchemy Session that automatically commits on success or rolls
98
+ back on exceptions. Re-entrant: nested calls reuse the same session. Use for
99
+ multi-statement transactions; prefer `query()` for single statements.
100
+
101
+ Yields
102
+ ------
103
+ Session
104
+ SQLAlchemy session. Commits on context exit, rolls back on exceptions.
105
+
106
+ Examples
107
+ --------
108
+ with self.session() as session:
109
+ session.execute(text("DELETE FROM t WHERE id = :id"), {"id": 1})
110
+ session.execute(text("INSERT INTO t2 SELECT * FROM t"))
111
+ """
112
+ existing = _current_session.get()
113
+
114
+ # Re-entrant: reuse the session if in a session context, no begin()
115
+ if existing is not None:
116
+ yield existing
117
+ return
118
+
119
+ if self._session_factory is None:
120
+ raise AttributeError("Database not initialized. Call initialize() first.")
121
+
122
+ # Create new session; outermost scope owns the transaction
123
+ session = self._session_factory()
124
+ token = _current_session.set(session)
125
+
126
+ try:
127
+ with session.begin():
128
+ yield session
129
+ finally:
130
+ _current_session.reset(token)
131
+ session.close()
132
+
133
+ def get_metadata(self) -> MetaData | None:
134
+ """Return the MetaData object for this class.
135
+
136
+ Subclasses can override this to provide their SQLAlchemy MetaData.
137
+ The base implementation returns None.
138
+
139
+ Returns
140
+ -------
141
+ MetaData | None
142
+ SQLAlchemy MetaData object for this class.
143
+ """
144
+ return None
145
+
146
+ def initialize(self, log_queries: bool = False) -> list[str]:
147
+ """Connect to the DB and create tables if needed.
148
+
149
+ This method creates the SQLAlchemy engine and session factory,
150
+ and creates tables returned by `get_metadata()`.
151
+
152
+ Parameters
153
+ ----------
154
+ log_queries : bool
155
+ Log each query which is executed.
156
+
157
+ Returns
158
+ -------
159
+ list[str]
160
+ The list of all tables in the DB.
161
+ """
162
+ # Create engine with SQLite-specific settings
163
+ engine_kwargs: dict[str, Any] = {
164
+ # SQLite needs check_same_thread=False for multi-threaded access
165
+ "connect_args": {"check_same_thread": False}
166
+ }
167
+ self._engine = create_engine(self.database_url, **engine_kwargs)
168
+
169
+ # Set SQLite pragmas via event listener for optimal performance and correctness
170
+ event.listen(self._engine, "connect", _set_sqlite_pragmas)
171
+
172
+ if log_queries:
173
+ # Set up query logging via event listener
174
+ event.listen(self._engine, "before_cursor_execute", _log_query)
175
+
176
+ # Create session factory
177
+ self._session_factory = sessionmaker(bind=self._engine)
178
+
179
+ # Create tables defined in metadata (idempotent - only creates missing tables)
180
+ if (metadata := self.get_metadata()) is not None:
181
+ metadata.create_all(self._engine)
182
+
183
+ # Get all table names using inspector
184
+ inspector = inspect(self._engine)
185
+ return inspector.get_table_names()
186
+
187
+ def query(
188
+ self,
189
+ query: str,
190
+ data: Sequence[dict[str, Any]] | dict[str, Any] | None = None,
191
+ ) -> list[dict[str, Any]]:
192
+ """Execute a SQL query and return the results as list of dicts.
193
+
194
+ TRANSACTION SEMANTICS:
195
+ ----------------------
196
+ If called outside a session context, each call to query() runs in its own
197
+ isolated transaction that is automatically committed. This is suitable for
198
+ single SQL statements.
199
+
200
+ If called within a session() context, query() reuses the existing session
201
+ and transaction. This enables atomic multi-query operations:
202
+
203
+ with self.session() as session:
204
+ self.query("UPDATE ...", {...}) # Shares same transaction
205
+ self.query("INSERT ...", {...}) # Shares same transaction
206
+ # Both succeed or fail together
207
+
208
+ You can also use session.execute() directly for the same effect:
209
+
210
+ with self.session() as session:
211
+ session.execute(text("UPDATE ..."), {...})
212
+ session.execute(text("INSERT ..."), {...})
213
+
214
+ Parameters
215
+ ----------
216
+ query : str
217
+ SQL query string with named parameter placeholders.
218
+ Use :name syntax for parameters: "SELECT * FROM t WHERE a = :a AND b = :b"
219
+ data : Sequence[dict[str, Any]] | dict[str, Any] | None
220
+ Query parameters using named parameter syntax:
221
+ - Single execution: pass dict, e.g., {"a": value1, "b": value2}
222
+ - Batch execution: pass sequence of dicts, e.g., [{"a": 1}, {"a": 2}]
223
+
224
+ Returns
225
+ -------
226
+ list[dict[str, Any]]
227
+ Query results as a list of dictionaries.
228
+
229
+ Examples
230
+ --------
231
+ # Single query with named parameters (auto-committed transaction)
232
+ rows = self.query(
233
+ "SELECT * FROM node WHERE node_id = :id AND status = :status",
234
+ {"id": node_id, "status": status}
235
+ )
236
+
237
+ # Batch insert with named parameters (auto-committed transaction)
238
+ rows = self.query(
239
+ "INSERT INTO node (node_id, status) VALUES (:id, :status)",
240
+ [{"id": 1, "status": "online"}, {"id": 2, "status": "offline"}]
241
+ )
242
+
243
+ # Multi-statement transaction - query() calls share the same session
244
+ with self.session():
245
+ # Both statements succeed or fail together
246
+ self.query("DELETE FROM token_store WHERE active_until < :time", {...})
247
+ self.query("UPDATE run SET status = :status WHERE id = :id", {...})
248
+
249
+ # Nested session() - query() calls share the same session
250
+ with self.session():
251
+ self.query("DELETE FROM token_store WHERE active_until < :time", {...})
252
+ with self.session():
253
+ self.query("UPDATE run SET status = :status WHERE id = :id", {...})
254
+ """
255
+ if self._engine is None:
256
+ raise AttributeError(
257
+ "LinkState is not initialized. Call initialize() first."
258
+ )
259
+
260
+ if data is None:
261
+ data = {}
262
+
263
+ # Clean up whitespace to make the logs nicer
264
+ query = re.sub(r"\s+", " ", query.strip())
265
+
266
+ try:
267
+ # Wrap query in text() to enable SQLAlchemy named parameter syntax (:param).
268
+ sql = text(query)
269
+
270
+ def execute_and_fetch(session: Session) -> list[dict[str, Any]]:
271
+ """Execute query and fetch results from the given session."""
272
+ # Execute query (results live in database cursor).
273
+ # There is no need to check for batch vs single execution;
274
+ # SQLAlchemy handles both cases automatically.
275
+ result = session.execute(sql, data)
276
+
277
+ # Fetch results into Python memory before commit.
278
+ # mappings() returns dict-like rows (works for SELECT and RETURNING).
279
+ if result.returns_rows: # type: ignore
280
+ return [dict(row) for row in result.mappings()]
281
+
282
+ # For statements without RETURNING (INSERT/UPDATE/DELETE),
283
+ # returns_rows is False, so we return empty list.
284
+ return []
285
+
286
+ # Not in a session context, create a new session context for this query
287
+ with self.session() as session:
288
+ return execute_and_fetch(session)
289
+
290
+ except SQLAlchemyError as exc:
291
+ log(ERROR, {"query": query, "data": data, "exception": exc})
292
+ raise
@@ -0,0 +1,156 @@
1
+ # Copyright 2025 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
+ """Mixin providing common SQLite connection and initialization logic."""
16
+
17
+
18
+ import re
19
+ import sqlite3
20
+ from abc import ABC
21
+ from collections.abc import Sequence
22
+ from logging import DEBUG, ERROR
23
+ from typing import Any
24
+
25
+ from flwr.common.logger import log
26
+ from flwr.supercore.constant import SQLITE_PRAGMAS
27
+
28
+ DictOrTuple = tuple[Any, ...] | dict[str, Any]
29
+
30
+
31
+ class SqliteMixin(ABC):
32
+ """Mixin providing common SQLite connection and initialization logic."""
33
+
34
+ def __init__(self, database_path: str) -> None:
35
+ self.database_path = database_path
36
+ self._conn: sqlite3.Connection | None = None
37
+
38
+ @property
39
+ def conn(self) -> sqlite3.Connection:
40
+ """Get the SQLite connection."""
41
+ if self._conn is None:
42
+ raise AttributeError("Database not initialized. Call initialize() first.")
43
+ return self._conn
44
+
45
+ def get_sql_statements(self) -> tuple[str, ...]:
46
+ """Return SQL statements for this class.
47
+
48
+ Subclasses can override this to provide their SQL CREATE statements.
49
+ The base implementation returns an empty tuple.
50
+
51
+ Returns
52
+ -------
53
+ tuple[str, ...]
54
+ SQL CREATE TABLE/INDEX statements for this class.
55
+ """
56
+ return ()
57
+
58
+ def initialize(self, log_queries: bool = False) -> list[tuple[str]]:
59
+ """Connect to the DB, enable FK support, and create tables if needed.
60
+
61
+ This method executes SQL statements returned by `get_sql_statements()`.
62
+
63
+ Parameters
64
+ ----------
65
+ log_queries : bool
66
+ Log each query which is executed.
67
+
68
+ Returns
69
+ -------
70
+ list[tuple[str]]
71
+ The list of all tables in the DB.
72
+
73
+ Examples
74
+ --------
75
+ Override `get_sql_statements()` in your subclass:
76
+
77
+ .. code:: python
78
+
79
+ def get_sql_statements(self) -> tuple[str, ...]:
80
+ return (
81
+ SQL_CREATE_TABLE_FOO,
82
+ SQL_CREATE_TABLE_BAR,
83
+ )
84
+
85
+ To include parent SQL statements, call super():
86
+
87
+ .. code:: python
88
+
89
+ def get_sql_statements(self) -> tuple[str, ...]:
90
+ return super().get_sql_statements() + (
91
+ SQL_CREATE_TABLE_FOO,
92
+ SQL_CREATE_TABLE_BAR,
93
+ )
94
+ """
95
+ self._conn = sqlite3.connect(self.database_path)
96
+ # Set SQLite pragmas for optimal performance and correctness
97
+ for pragma, value in SQLITE_PRAGMAS:
98
+ self._conn.execute(f"PRAGMA {pragma} = {value};")
99
+ self._conn.row_factory = dict_factory
100
+
101
+ if log_queries:
102
+ self._conn.set_trace_callback(lambda q: log(DEBUG, q))
103
+
104
+ # Create tables and indexes
105
+ cur = self._conn.cursor()
106
+ for sql in self.get_sql_statements():
107
+ cur.execute(sql)
108
+ res = cur.execute("SELECT name FROM sqlite_schema;")
109
+ return res.fetchall()
110
+
111
+ def query(
112
+ self,
113
+ query: str,
114
+ data: Sequence[DictOrTuple] | DictOrTuple | None = None,
115
+ ) -> list[dict[str, Any]]:
116
+ """Execute a SQL query and return the results as list of dicts."""
117
+ if self._conn is None:
118
+ raise AttributeError("LinkState is not initialized.")
119
+
120
+ if data is None:
121
+ data = []
122
+
123
+ # Clean up whitespace to make the logs nicer
124
+ query = re.sub(r"\s+", " ", query)
125
+
126
+ try:
127
+ with self._conn:
128
+ if (
129
+ len(data) > 0
130
+ and isinstance(data, (tuple | list))
131
+ and isinstance(data[0], (tuple | dict))
132
+ ):
133
+ rows = self._conn.executemany(query, data)
134
+ else:
135
+ rows = self._conn.execute(query, data)
136
+
137
+ # Extract results before committing to support
138
+ # INSERT/UPDATE ... RETURNING
139
+ # style queries
140
+ result = rows.fetchall()
141
+ except KeyError as exc:
142
+ log(ERROR, {"query": query, "data": data, "exception": exc})
143
+
144
+ return result
145
+
146
+
147
+ def dict_factory(
148
+ cursor: sqlite3.Cursor,
149
+ row: sqlite3.Row,
150
+ ) -> dict[str, Any]:
151
+ """Turn SQLite results into dicts.
152
+
153
+ Less efficent for retrival of large amounts of data but easier to use.
154
+ """
155
+ fields = [column[0] for column in cursor.description]
156
+ return dict(zip(fields, row, strict=True))
@@ -1,4 +1,4 @@
1
- # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2026 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  # ==============================================================================
15
- """Flower AppIO service."""
15
+ """Flower SuperCore state components."""
@@ -0,0 +1,125 @@
1
+ # State Entity Relationship Diagram
2
+
3
+ ## Schema
4
+
5
+ ```mermaid
6
+
7
+ ---
8
+ config:
9
+ layout: elk
10
+ ---
11
+ erDiagram
12
+ context {
13
+ INTEGER run_id FK "nullable"
14
+ BLOB context "nullable"
15
+ }
16
+
17
+ logs {
18
+ INTEGER run_id FK "nullable"
19
+ VARCHAR log "nullable"
20
+ INTEGER node_id "nullable"
21
+ FLOAT timestamp "nullable"
22
+ }
23
+
24
+ message_ins {
25
+ INTEGER run_id FK "nullable"
26
+ BLOB content "nullable"
27
+ FLOAT created_at "nullable"
28
+ VARCHAR delivered_at "nullable"
29
+ INTEGER dst_node_id "nullable"
30
+ BLOB error "nullable"
31
+ VARCHAR group_id "nullable"
32
+ VARCHAR message_id UK "nullable"
33
+ VARCHAR message_type "nullable"
34
+ VARCHAR reply_to_message_id "nullable"
35
+ INTEGER src_node_id "nullable"
36
+ FLOAT ttl "nullable"
37
+ }
38
+
39
+ message_res {
40
+ INTEGER run_id FK "nullable"
41
+ BLOB content "nullable"
42
+ FLOAT created_at "nullable"
43
+ VARCHAR delivered_at "nullable"
44
+ INTEGER dst_node_id "nullable"
45
+ BLOB error "nullable"
46
+ VARCHAR group_id "nullable"
47
+ VARCHAR message_id UK "nullable"
48
+ VARCHAR message_type "nullable"
49
+ VARCHAR reply_to_message_id "nullable"
50
+ INTEGER src_node_id "nullable"
51
+ FLOAT ttl "nullable"
52
+ }
53
+
54
+ node {
55
+ FLOAT heartbeat_interval "nullable"
56
+ VARCHAR last_activated_at "nullable"
57
+ VARCHAR last_deactivated_at "nullable"
58
+ INTEGER node_id UK "nullable"
59
+ TIMESTAMP online_until "nullable"
60
+ VARCHAR owner_aid "nullable"
61
+ VARCHAR owner_name "nullable"
62
+ BLOB public_key UK "nullable"
63
+ VARCHAR registered_at "nullable"
64
+ VARCHAR status "nullable"
65
+ VARCHAR unregistered_at "nullable"
66
+ }
67
+
68
+ object_children {
69
+ VARCHAR child_id PK,FK
70
+ VARCHAR parent_id PK,FK
71
+ }
72
+
73
+ objects {
74
+ VARCHAR object_id PK "nullable"
75
+ BLOB content "nullable"
76
+ INTEGER is_available
77
+ INTEGER ref_count
78
+ }
79
+
80
+ <<<<<<< HEAD
81
+ run {
82
+ INTEGER bytes_recv "nullable"
83
+ INTEGER bytes_sent "nullable"
84
+ FLOAT clientapp_runtime "nullable"
85
+ VARCHAR details "nullable"
86
+ VARCHAR fab_hash "nullable"
87
+ VARCHAR fab_id "nullable"
88
+ VARCHAR fab_version "nullable"
89
+ VARCHAR federation "nullable"
90
+ BLOB federation_options "nullable"
91
+ VARCHAR finished_at "nullable"
92
+ VARCHAR flwr_aid "nullable"
93
+ VARCHAR override_config "nullable"
94
+ VARCHAR pending_at "nullable"
95
+ INTEGER run_id UK "nullable"
96
+ VARCHAR running_at "nullable"
97
+ VARCHAR starting_at "nullable"
98
+ VARCHAR sub_status "nullable"
99
+ }
100
+
101
+ =======
102
+ >>>>>>> main
103
+ run_objects {
104
+ VARCHAR object_id PK,FK
105
+ INTEGER run_id PK
106
+ }
107
+
108
+ token_store {
109
+ INTEGER run_id PK "nullable"
110
+ FLOAT active_until "nullable"
111
+ VARCHAR token UK
112
+ }
113
+
114
+ <<<<<<< HEAD
115
+ run ||--o| context : run_id
116
+ run ||--o{ logs : run_id
117
+ run ||--o{ message_ins : run_id
118
+ run ||--o{ message_res : run_id
119
+ =======
120
+ >>>>>>> main
121
+ objects ||--o| object_children : parent_id
122
+ objects ||--o| object_children : child_id
123
+ objects ||--o| run_objects : object_id
124
+
125
+ ```
@@ -1,4 +1,4 @@
1
- # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2026 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  # ==============================================================================
15
- """Flower CLI `new` command templates."""
15
+ """Flower SQLAlchemy database schema."""
@@ -0,0 +1,36 @@
1
+ # Copyright 2026 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
+ """SQLAlchemy Core Table definitions for CoreState."""
16
+
17
+
18
+ from sqlalchemy import Column, Float, Integer, MetaData, String, Table
19
+
20
+
21
+ def create_corestate_metadata() -> MetaData:
22
+ """Create and return MetaData with CoreState table definitions."""
23
+ metadata = MetaData()
24
+
25
+ # --------------------------------------------------------------------------
26
+ # Table: token_store
27
+ # --------------------------------------------------------------------------
28
+ Table(
29
+ "token_store",
30
+ metadata,
31
+ Column("run_id", Integer, primary_key=True, nullable=True),
32
+ Column("token", String, unique=True, nullable=False),
33
+ Column("active_until", Float),
34
+ )
35
+
36
+ return metadata