flock-core 0.5.0b27__py3-none-any.whl → 0.5.0b50__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 flock-core might be problematic. Click here for more details.

Files changed (357) hide show
  1. flock/__init__.py +12 -217
  2. flock/agent.py +678 -0
  3. flock/api/themes.py +71 -0
  4. flock/artifacts.py +79 -0
  5. flock/cli.py +75 -0
  6. flock/components.py +173 -0
  7. flock/dashboard/__init__.py +28 -0
  8. flock/dashboard/collector.py +283 -0
  9. flock/dashboard/events.py +182 -0
  10. flock/dashboard/launcher.py +230 -0
  11. flock/dashboard/service.py +537 -0
  12. flock/dashboard/websocket.py +235 -0
  13. flock/engines/__init__.py +6 -0
  14. flock/engines/dspy_engine.py +856 -0
  15. flock/examples.py +128 -0
  16. flock/{core/util → helper}/cli_helper.py +4 -3
  17. flock/{core/logging → logging}/__init__.py +2 -3
  18. flock/{core/logging → logging}/formatters/enum_builder.py +3 -4
  19. flock/{core/logging → logging}/formatters/theme_builder.py +19 -44
  20. flock/{core/logging → logging}/formatters/themed_formatter.py +69 -115
  21. flock/{core/logging → logging}/logging.py +77 -61
  22. flock/{core/logging → logging}/telemetry.py +20 -26
  23. flock/{core/logging → logging}/telemetry_exporter/base_exporter.py +2 -2
  24. flock/{core/logging → logging}/telemetry_exporter/file_exporter.py +6 -9
  25. flock/{core/logging → logging}/telemetry_exporter/sqlite_exporter.py +2 -3
  26. flock/{core/logging → logging}/trace_and_logged.py +20 -24
  27. flock/mcp/__init__.py +91 -0
  28. flock/{core/mcp/mcp_client.py → mcp/client.py} +103 -154
  29. flock/{core/mcp/mcp_config.py → mcp/config.py} +62 -117
  30. flock/mcp/manager.py +255 -0
  31. flock/mcp/servers/sse/__init__.py +1 -1
  32. flock/mcp/servers/sse/flock_sse_server.py +11 -53
  33. flock/mcp/servers/stdio/__init__.py +1 -1
  34. flock/mcp/servers/stdio/flock_stdio_server.py +8 -48
  35. flock/mcp/servers/streamable_http/flock_streamable_http_server.py +17 -62
  36. flock/mcp/servers/websockets/flock_websocket_server.py +7 -40
  37. flock/{core/mcp/flock_mcp_tool.py → mcp/tool.py} +16 -26
  38. flock/mcp/types/__init__.py +42 -0
  39. flock/{core/mcp → mcp}/types/callbacks.py +9 -15
  40. flock/{core/mcp → mcp}/types/factories.py +7 -6
  41. flock/{core/mcp → mcp}/types/handlers.py +13 -18
  42. flock/{core/mcp → mcp}/types/types.py +70 -74
  43. flock/{core/mcp → mcp}/util/helpers.py +1 -1
  44. flock/orchestrator.py +645 -0
  45. flock/registry.py +148 -0
  46. flock/runtime.py +262 -0
  47. flock/service.py +140 -0
  48. flock/store.py +69 -0
  49. flock/subscription.py +111 -0
  50. flock/themes/andromeda.toml +1 -1
  51. flock/themes/apple-system-colors.toml +1 -1
  52. flock/themes/arcoiris.toml +1 -1
  53. flock/themes/atomonelight.toml +1 -1
  54. flock/themes/ayu copy.toml +1 -1
  55. flock/themes/ayu-light.toml +1 -1
  56. flock/themes/belafonte-day.toml +1 -1
  57. flock/themes/belafonte-night.toml +1 -1
  58. flock/themes/blulocodark.toml +1 -1
  59. flock/themes/breeze.toml +1 -1
  60. flock/themes/broadcast.toml +1 -1
  61. flock/themes/brogrammer.toml +1 -1
  62. flock/themes/builtin-dark.toml +1 -1
  63. flock/themes/builtin-pastel-dark.toml +1 -1
  64. flock/themes/catppuccin-latte.toml +1 -1
  65. flock/themes/catppuccin-macchiato.toml +1 -1
  66. flock/themes/catppuccin-mocha.toml +1 -1
  67. flock/themes/cga.toml +1 -1
  68. flock/themes/chalk.toml +1 -1
  69. flock/themes/ciapre.toml +1 -1
  70. flock/themes/coffee-theme.toml +1 -1
  71. flock/themes/cyberpunkscarletprotocol.toml +1 -1
  72. flock/themes/dark+.toml +1 -1
  73. flock/themes/darkermatrix.toml +1 -1
  74. flock/themes/darkside.toml +1 -1
  75. flock/themes/desert.toml +1 -1
  76. flock/themes/django.toml +1 -1
  77. flock/themes/djangosmooth.toml +1 -1
  78. flock/themes/doomone.toml +1 -1
  79. flock/themes/dotgov.toml +1 -1
  80. flock/themes/dracula+.toml +1 -1
  81. flock/themes/duckbones.toml +1 -1
  82. flock/themes/encom.toml +1 -1
  83. flock/themes/espresso.toml +1 -1
  84. flock/themes/everblush.toml +1 -1
  85. flock/themes/fairyfloss.toml +1 -1
  86. flock/themes/fideloper.toml +1 -1
  87. flock/themes/fishtank.toml +1 -1
  88. flock/themes/flexoki-light.toml +1 -1
  89. flock/themes/floraverse.toml +1 -1
  90. flock/themes/framer.toml +1 -1
  91. flock/themes/galizur.toml +1 -1
  92. flock/themes/github.toml +1 -1
  93. flock/themes/grass.toml +1 -1
  94. flock/themes/grey-green.toml +1 -1
  95. flock/themes/gruvboxlight.toml +1 -1
  96. flock/themes/guezwhoz.toml +1 -1
  97. flock/themes/harper.toml +1 -1
  98. flock/themes/hax0r-blue.toml +1 -1
  99. flock/themes/hopscotch.256.toml +1 -1
  100. flock/themes/ic-green-ppl.toml +1 -1
  101. flock/themes/iceberg-dark.toml +1 -1
  102. flock/themes/japanesque.toml +1 -1
  103. flock/themes/jubi.toml +1 -1
  104. flock/themes/kibble.toml +1 -1
  105. flock/themes/kolorit.toml +1 -1
  106. flock/themes/kurokula.toml +1 -1
  107. flock/themes/materialdesigncolors.toml +1 -1
  108. flock/themes/matrix.toml +1 -1
  109. flock/themes/mellifluous.toml +1 -1
  110. flock/themes/midnight-in-mojave.toml +1 -1
  111. flock/themes/monokai-remastered.toml +1 -1
  112. flock/themes/monokai-soda.toml +1 -1
  113. flock/themes/neon.toml +1 -1
  114. flock/themes/neopolitan.toml +1 -1
  115. flock/themes/nord-light.toml +1 -1
  116. flock/themes/ocean.toml +1 -1
  117. flock/themes/onehalfdark.toml +1 -1
  118. flock/themes/onehalflight.toml +1 -1
  119. flock/themes/palenighthc.toml +1 -1
  120. flock/themes/paulmillr.toml +1 -1
  121. flock/themes/pencildark.toml +1 -1
  122. flock/themes/pnevma.toml +1 -1
  123. flock/themes/purple-rain.toml +1 -1
  124. flock/themes/purplepeter.toml +1 -1
  125. flock/themes/raycast-dark.toml +1 -1
  126. flock/themes/red-sands.toml +1 -1
  127. flock/themes/relaxed.toml +1 -1
  128. flock/themes/retro.toml +1 -1
  129. flock/themes/rose-pine.toml +1 -1
  130. flock/themes/royal.toml +1 -1
  131. flock/themes/ryuuko.toml +1 -1
  132. flock/themes/sakura.toml +1 -1
  133. flock/themes/scarlet-protocol.toml +1 -1
  134. flock/themes/seoulbones-dark.toml +1 -1
  135. flock/themes/shades-of-purple.toml +1 -1
  136. flock/themes/smyck.toml +1 -1
  137. flock/themes/softserver.toml +1 -1
  138. flock/themes/solarized-darcula.toml +1 -1
  139. flock/themes/square.toml +1 -1
  140. flock/themes/sugarplum.toml +1 -1
  141. flock/themes/thayer-bright.toml +1 -1
  142. flock/themes/tokyonight.toml +1 -1
  143. flock/themes/tomorrow.toml +1 -1
  144. flock/themes/ubuntu.toml +1 -1
  145. flock/themes/ultradark.toml +1 -1
  146. flock/themes/ultraviolent.toml +1 -1
  147. flock/themes/unikitty.toml +1 -1
  148. flock/themes/urple.toml +1 -1
  149. flock/themes/vesper.toml +1 -1
  150. flock/themes/vimbones.toml +1 -1
  151. flock/themes/wildcherry.toml +1 -1
  152. flock/themes/wilmersdorf.toml +1 -1
  153. flock/themes/wryan.toml +1 -1
  154. flock/themes/xcodedarkhc.toml +1 -1
  155. flock/themes/xcodelight.toml +1 -1
  156. flock/themes/zenbones-light.toml +1 -1
  157. flock/themes/zenwritten-dark.toml +1 -1
  158. flock/utilities.py +301 -0
  159. flock/{components/utility → utility}/output_utility_component.py +68 -53
  160. flock/visibility.py +107 -0
  161. flock_core-0.5.0b50.dist-info/METADATA +747 -0
  162. flock_core-0.5.0b50.dist-info/RECORD +398 -0
  163. flock_core-0.5.0b50.dist-info/entry_points.txt +2 -0
  164. {flock_core-0.5.0b27.dist-info → flock_core-0.5.0b50.dist-info}/licenses/LICENSE +1 -1
  165. flock/adapter/__init__.py +0 -14
  166. flock/adapter/azure_adapter.py +0 -68
  167. flock/adapter/chroma_adapter.py +0 -73
  168. flock/adapter/faiss_adapter.py +0 -97
  169. flock/adapter/pinecone_adapter.py +0 -51
  170. flock/adapter/vector_base.py +0 -47
  171. flock/cli/assets/release_notes.md +0 -140
  172. flock/cli/config.py +0 -8
  173. flock/cli/constants.py +0 -36
  174. flock/cli/create_agent.py +0 -1
  175. flock/cli/create_flock.py +0 -280
  176. flock/cli/execute_flock.py +0 -620
  177. flock/cli/load_agent.py +0 -1
  178. flock/cli/load_examples.py +0 -1
  179. flock/cli/load_flock.py +0 -192
  180. flock/cli/load_release_notes.py +0 -20
  181. flock/cli/loaded_flock_cli.py +0 -254
  182. flock/cli/manage_agents.py +0 -459
  183. flock/cli/registry_management.py +0 -889
  184. flock/cli/runner.py +0 -41
  185. flock/cli/settings.py +0 -857
  186. flock/cli/utils.py +0 -135
  187. flock/cli/view_results.py +0 -29
  188. flock/cli/yaml_editor.py +0 -396
  189. flock/components/__init__.py +0 -30
  190. flock/components/evaluation/__init__.py +0 -9
  191. flock/components/evaluation/declarative_evaluation_component.py +0 -606
  192. flock/components/routing/__init__.py +0 -15
  193. flock/components/routing/conditional_routing_component.py +0 -494
  194. flock/components/routing/default_routing_component.py +0 -103
  195. flock/components/routing/llm_routing_component.py +0 -206
  196. flock/components/utility/__init__.py +0 -15
  197. flock/components/utility/memory_utility_component.py +0 -550
  198. flock/components/utility/metrics_utility_component.py +0 -700
  199. flock/config.py +0 -61
  200. flock/core/__init__.py +0 -110
  201. flock/core/agent/__init__.py +0 -16
  202. flock/core/agent/default_agent.py +0 -180
  203. flock/core/agent/flock_agent_components.py +0 -104
  204. flock/core/agent/flock_agent_execution.py +0 -101
  205. flock/core/agent/flock_agent_integration.py +0 -260
  206. flock/core/agent/flock_agent_lifecycle.py +0 -186
  207. flock/core/agent/flock_agent_serialization.py +0 -381
  208. flock/core/api/__init__.py +0 -10
  209. flock/core/api/custom_endpoint.py +0 -45
  210. flock/core/api/endpoints.py +0 -254
  211. flock/core/api/main.py +0 -162
  212. flock/core/api/models.py +0 -97
  213. flock/core/api/run_store.py +0 -224
  214. flock/core/api/runner.py +0 -44
  215. flock/core/api/service.py +0 -214
  216. flock/core/component/__init__.py +0 -15
  217. flock/core/component/agent_component_base.py +0 -309
  218. flock/core/component/evaluation_component.py +0 -62
  219. flock/core/component/routing_component.py +0 -74
  220. flock/core/component/utility_component.py +0 -69
  221. flock/core/config/flock_agent_config.py +0 -58
  222. flock/core/config/scheduled_agent_config.py +0 -40
  223. flock/core/context/context.py +0 -213
  224. flock/core/context/context_manager.py +0 -37
  225. flock/core/context/context_vars.py +0 -10
  226. flock/core/evaluation/utils.py +0 -396
  227. flock/core/execution/batch_executor.py +0 -369
  228. flock/core/execution/evaluation_executor.py +0 -438
  229. flock/core/execution/local_executor.py +0 -31
  230. flock/core/execution/opik_executor.py +0 -103
  231. flock/core/execution/temporal_executor.py +0 -164
  232. flock/core/flock.py +0 -634
  233. flock/core/flock_agent.py +0 -336
  234. flock/core/flock_factory.py +0 -551
  235. flock/core/flock_scheduler.py +0 -166
  236. flock/core/flock_server_manager.py +0 -136
  237. flock/core/interpreter/python_interpreter.py +0 -689
  238. flock/core/mcp/__init__.py +0 -1
  239. flock/core/mcp/flock_mcp_server.py +0 -680
  240. flock/core/mcp/mcp_client_manager.py +0 -201
  241. flock/core/mcp/types/__init__.py +0 -1
  242. flock/core/mixin/dspy_integration.py +0 -403
  243. flock/core/mixin/prompt_parser.py +0 -125
  244. flock/core/orchestration/__init__.py +0 -15
  245. flock/core/orchestration/flock_batch_processor.py +0 -94
  246. flock/core/orchestration/flock_evaluator.py +0 -113
  247. flock/core/orchestration/flock_execution.py +0 -295
  248. flock/core/orchestration/flock_initialization.py +0 -149
  249. flock/core/orchestration/flock_server_manager.py +0 -67
  250. flock/core/orchestration/flock_web_server.py +0 -117
  251. flock/core/registry/__init__.py +0 -45
  252. flock/core/registry/agent_registry.py +0 -69
  253. flock/core/registry/callable_registry.py +0 -139
  254. flock/core/registry/component_discovery.py +0 -142
  255. flock/core/registry/component_registry.py +0 -64
  256. flock/core/registry/config_mapping.py +0 -64
  257. flock/core/registry/decorators.py +0 -137
  258. flock/core/registry/registry_hub.py +0 -205
  259. flock/core/registry/server_registry.py +0 -57
  260. flock/core/registry/type_registry.py +0 -86
  261. flock/core/serialization/__init__.py +0 -13
  262. flock/core/serialization/callable_registry.py +0 -52
  263. flock/core/serialization/flock_serializer.py +0 -832
  264. flock/core/serialization/json_encoder.py +0 -41
  265. flock/core/serialization/secure_serializer.py +0 -175
  266. flock/core/serialization/serializable.py +0 -342
  267. flock/core/serialization/serialization_utils.py +0 -412
  268. flock/core/util/file_path_utils.py +0 -223
  269. flock/core/util/hydrator.py +0 -309
  270. flock/core/util/input_resolver.py +0 -164
  271. flock/core/util/loader.py +0 -59
  272. flock/core/util/splitter.py +0 -219
  273. flock/di.py +0 -27
  274. flock/platform/docker_tools.py +0 -49
  275. flock/platform/jaeger_install.py +0 -86
  276. flock/webapp/__init__.py +0 -1
  277. flock/webapp/app/__init__.py +0 -0
  278. flock/webapp/app/api/__init__.py +0 -0
  279. flock/webapp/app/api/agent_management.py +0 -241
  280. flock/webapp/app/api/execution.py +0 -709
  281. flock/webapp/app/api/flock_management.py +0 -129
  282. flock/webapp/app/api/registry_viewer.py +0 -30
  283. flock/webapp/app/chat.py +0 -665
  284. flock/webapp/app/config.py +0 -104
  285. flock/webapp/app/dependencies.py +0 -117
  286. flock/webapp/app/main.py +0 -1070
  287. flock/webapp/app/middleware.py +0 -113
  288. flock/webapp/app/models_ui.py +0 -7
  289. flock/webapp/app/services/__init__.py +0 -0
  290. flock/webapp/app/services/feedback_file_service.py +0 -363
  291. flock/webapp/app/services/flock_service.py +0 -337
  292. flock/webapp/app/services/sharing_models.py +0 -81
  293. flock/webapp/app/services/sharing_store.py +0 -598
  294. flock/webapp/app/templates/theme_mapper.html +0 -326
  295. flock/webapp/app/theme_mapper.py +0 -812
  296. flock/webapp/app/utils.py +0 -85
  297. flock/webapp/run.py +0 -215
  298. flock/webapp/static/css/chat.css +0 -301
  299. flock/webapp/static/css/components.css +0 -167
  300. flock/webapp/static/css/header.css +0 -39
  301. flock/webapp/static/css/layout.css +0 -46
  302. flock/webapp/static/css/sidebar.css +0 -127
  303. flock/webapp/static/css/two-pane.css +0 -48
  304. flock/webapp/templates/base.html +0 -200
  305. flock/webapp/templates/chat.html +0 -152
  306. flock/webapp/templates/chat_settings.html +0 -19
  307. flock/webapp/templates/flock_editor.html +0 -16
  308. flock/webapp/templates/index.html +0 -12
  309. flock/webapp/templates/partials/_agent_detail_form.html +0 -93
  310. flock/webapp/templates/partials/_agent_list.html +0 -18
  311. flock/webapp/templates/partials/_agent_manager_view.html +0 -51
  312. flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
  313. flock/webapp/templates/partials/_chat_container.html +0 -15
  314. flock/webapp/templates/partials/_chat_messages.html +0 -57
  315. flock/webapp/templates/partials/_chat_settings_form.html +0 -85
  316. flock/webapp/templates/partials/_create_flock_form.html +0 -50
  317. flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
  318. flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
  319. flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
  320. flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
  321. flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
  322. flock/webapp/templates/partials/_env_vars_table.html +0 -23
  323. flock/webapp/templates/partials/_execution_form.html +0 -118
  324. flock/webapp/templates/partials/_execution_view_container.html +0 -28
  325. flock/webapp/templates/partials/_flock_file_list.html +0 -23
  326. flock/webapp/templates/partials/_flock_properties_form.html +0 -52
  327. flock/webapp/templates/partials/_flock_upload_form.html +0 -16
  328. flock/webapp/templates/partials/_header_flock_status.html +0 -5
  329. flock/webapp/templates/partials/_load_manager_view.html +0 -49
  330. flock/webapp/templates/partials/_registry_table.html +0 -25
  331. flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
  332. flock/webapp/templates/partials/_results_display.html +0 -78
  333. flock/webapp/templates/partials/_settings_env_content.html +0 -9
  334. flock/webapp/templates/partials/_settings_theme_content.html +0 -14
  335. flock/webapp/templates/partials/_settings_view.html +0 -36
  336. flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
  337. flock/webapp/templates/partials/_share_link_snippet.html +0 -35
  338. flock/webapp/templates/partials/_sidebar.html +0 -74
  339. flock/webapp/templates/partials/_streaming_results_container.html +0 -195
  340. flock/webapp/templates/partials/_structured_data_view.html +0 -40
  341. flock/webapp/templates/partials/_theme_preview.html +0 -36
  342. flock/webapp/templates/registry_viewer.html +0 -84
  343. flock/webapp/templates/shared_run_page.html +0 -140
  344. flock/workflow/__init__.py +0 -0
  345. flock/workflow/activities.py +0 -196
  346. flock/workflow/agent_activities.py +0 -24
  347. flock/workflow/agent_execution_activity.py +0 -202
  348. flock/workflow/flock_workflow.py +0 -214
  349. flock/workflow/temporal_config.py +0 -96
  350. flock/workflow/temporal_setup.py +0 -68
  351. flock_core-0.5.0b27.dist-info/METADATA +0 -274
  352. flock_core-0.5.0b27.dist-info/RECORD +0 -559
  353. flock_core-0.5.0b27.dist-info/entry_points.txt +0 -2
  354. /flock/{core/logging → logging}/formatters/themes.py +0 -0
  355. /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
  356. /flock/{core/mcp → mcp}/util/__init__.py +0 -0
  357. {flock_core-0.5.0b27.dist-info → flock_core-0.5.0b50.dist-info}/WHEEL +0 -0
@@ -1,41 +0,0 @@
1
- """JSON encoder utilities for Flock objects."""
2
-
3
- import json
4
- from datetime import datetime
5
- from typing import Any
6
-
7
-
8
- class FlockJSONEncoder(json.JSONEncoder):
9
- """Custom JSON encoder for handling Pydantic models and other non-serializable objects."""
10
-
11
- def default(self, obj: Any) -> Any:
12
- from pydantic import BaseModel
13
-
14
- # Handle Pydantic models
15
- if isinstance(obj, BaseModel):
16
- return obj.model_dump()
17
-
18
- # Handle datetime objects
19
- if isinstance(obj, datetime):
20
- return obj.isoformat()
21
-
22
- # Handle sets, convert to list
23
- if isinstance(obj, set):
24
- return list(obj)
25
-
26
- # Handle objects with a to_dict method
27
- if hasattr(obj, "to_dict") and callable(getattr(obj, "to_dict")):
28
- return obj.to_dict()
29
-
30
- # Handle objects with a __dict__ attribute
31
- if hasattr(obj, "__dict__"):
32
- return {
33
- k: v for k, v in obj.__dict__.items() if not k.startswith("_")
34
- }
35
-
36
- # Let the parent class handle it or raise TypeError
37
- try:
38
- return super().default(obj)
39
- except TypeError:
40
- # If all else fails, convert to string
41
- return str(obj)
@@ -1,175 +0,0 @@
1
- import cloudpickle
2
-
3
-
4
- class SecureSerializer:
5
- """Security-focused serialization system with capability controls for Flock objects."""
6
-
7
- # Define capability levels for different modules
8
- MODULE_CAPABILITIES = {
9
- # Core Python - unrestricted
10
- "builtins": "unrestricted",
11
- "datetime": "unrestricted",
12
- "re": "unrestricted",
13
- "math": "unrestricted",
14
- "json": "unrestricted",
15
- # Framework modules - unrestricted
16
- "flock": "unrestricted",
17
- # System modules - restricted but allowed
18
- "os": "restricted",
19
- "io": "restricted",
20
- "sys": "restricted",
21
- "subprocess": "high_risk",
22
- # Network modules - high risk
23
- "socket": "high_risk",
24
- "requests": "high_risk",
25
- }
26
-
27
- # Functions that should never be serialized
28
- BLOCKED_FUNCTIONS = {
29
- "os.system",
30
- "os.popen",
31
- "os.spawn",
32
- "os.exec",
33
- "subprocess.call",
34
- "subprocess.run",
35
- "subprocess.Popen",
36
- "eval",
37
- "exec",
38
- "__import__",
39
- }
40
-
41
- @staticmethod
42
- def _get_module_capability(module_name):
43
- """Get the capability level for a module."""
44
- for prefix, level in SecureSerializer.MODULE_CAPABILITIES.items():
45
- if module_name == prefix or module_name.startswith(f"{prefix}."):
46
- return level
47
- return "unknown" # Default to unknown for unlisted modules
48
-
49
- @staticmethod
50
- def _is_safe_callable(obj):
51
- """Check if a callable is safe to serialize."""
52
- if not callable(obj) or isinstance(obj, type):
53
- return True, "Not a callable function"
54
-
55
- module = obj.__module__
56
- func_name = (
57
- f"{module}.{obj.__name__}"
58
- if hasattr(obj, "__name__")
59
- else "unknown"
60
- )
61
-
62
- # Check against blocked functions
63
- if func_name in SecureSerializer.BLOCKED_FUNCTIONS:
64
- return False, f"Function {func_name} is explicitly blocked"
65
-
66
- # Check module capability level
67
- capability = SecureSerializer._get_module_capability(module)
68
- if capability == "unknown":
69
- return False, f"Module {module} has unknown security capability"
70
-
71
- return True, capability
72
-
73
- @staticmethod
74
- def serialize(obj, allow_restricted=True, allow_high_risk=False):
75
- """Serialize an object with capability checks."""
76
- if callable(obj) and not isinstance(obj, type):
77
- is_safe, capability = SecureSerializer._is_safe_callable(obj)
78
-
79
- if not is_safe:
80
- raise ValueError(
81
- f"Cannot serialize unsafe callable: {capability}"
82
- )
83
-
84
- if capability == "high_risk" and not allow_high_risk:
85
- raise ValueError(
86
- f"High risk callable {obj.__module__}.{obj.__name__} requires explicit permission"
87
- )
88
-
89
- if capability == "restricted" and not allow_restricted:
90
- raise ValueError(
91
- f"Restricted callable {obj.__module__}.{obj.__name__} requires explicit permission"
92
- )
93
-
94
- # Store metadata about the callable for verification during deserialization
95
- metadata = {
96
- "module": obj.__module__,
97
- "name": getattr(obj, "__name__", "unknown"),
98
- "capability": capability,
99
- }
100
-
101
- return {
102
- "__serialized_callable__": True,
103
- "data": cloudpickle.dumps(obj).hex(),
104
- "metadata": metadata,
105
- }
106
-
107
- if isinstance(obj, list):
108
- return [
109
- SecureSerializer.serialize(
110
- item, allow_restricted, allow_high_risk
111
- )
112
- for item in obj
113
- ]
114
-
115
- if isinstance(obj, dict):
116
- return {
117
- k: SecureSerializer.serialize(
118
- v, allow_restricted, allow_high_risk
119
- )
120
- for k, v in obj.items()
121
- }
122
-
123
- return obj
124
-
125
- @staticmethod
126
- def deserialize(obj, allow_restricted=True, allow_high_risk=False):
127
- """Deserialize an object with capability enforcement."""
128
- if isinstance(obj, dict) and obj.get("__serialized_callable__") is True:
129
- # Validate the capability level during deserialization
130
- metadata = obj.get("metadata", {})
131
- capability = metadata.get("capability", "unknown")
132
-
133
- if capability == "high_risk" and not allow_high_risk:
134
- raise ValueError(
135
- f"Cannot deserialize high risk callable {metadata.get('module')}.{metadata.get('name')}"
136
- )
137
-
138
- if capability == "restricted" and not allow_restricted:
139
- raise ValueError(
140
- f"Cannot deserialize restricted callable {metadata.get('module')}.{metadata.get('name')}"
141
- )
142
-
143
- try:
144
- callable_obj = cloudpickle.loads(bytes.fromhex(obj["data"]))
145
-
146
- # Additional verification that the deserialized object matches its metadata
147
- if callable_obj.__module__ != metadata.get("module") or (
148
- hasattr(callable_obj, "__name__")
149
- and callable_obj.__name__ != metadata.get("name")
150
- ):
151
- raise ValueError(
152
- "Callable metadata mismatch - possible tampering detected"
153
- )
154
-
155
- return callable_obj
156
- except Exception as e:
157
- raise ValueError(f"Failed to deserialize callable: {e!s}")
158
-
159
- if isinstance(obj, list):
160
- return [
161
- SecureSerializer.deserialize(
162
- item, allow_restricted, allow_high_risk
163
- )
164
- for item in obj
165
- ]
166
-
167
- if isinstance(obj, dict) and "__serialized_callable__" not in obj:
168
- return {
169
- k: SecureSerializer.deserialize(
170
- v, allow_restricted, allow_high_risk
171
- )
172
- for k, v in obj.items()
173
- }
174
-
175
- return obj
@@ -1,342 +0,0 @@
1
- # src/flock/core/serialization/serializable.py
2
- import json
3
- from abc import ABC, abstractmethod
4
- from pathlib import Path
5
- from typing import Any, Literal, TypeVar
6
-
7
- # Use yaml if available, otherwise skip yaml methods
8
- try:
9
- import yaml
10
-
11
- YAML_AVAILABLE = True
12
- except ImportError:
13
- YAML_AVAILABLE = False
14
-
15
- # Use msgpack if available
16
- try:
17
- import msgpack
18
-
19
- MSGPACK_AVAILABLE = True
20
- except ImportError:
21
- MSGPACK_AVAILABLE = False
22
-
23
- # Use cloudpickle
24
- try:
25
- import cloudpickle
26
-
27
- PICKLE_AVAILABLE = True
28
- except ImportError:
29
- PICKLE_AVAILABLE = False
30
-
31
-
32
- T = TypeVar("T", bound="Serializable")
33
-
34
-
35
- class Serializable(ABC):
36
- """Base class for all serializable objects in the system.
37
-
38
- Provides methods for serializing/deserializing objects to various formats.
39
- Subclasses MUST implement to_dict and from_dict.
40
- """
41
-
42
- @abstractmethod
43
- def to_dict(self) -> dict[str, Any]:
44
- """Convert instance to a dictionary representation suitable for serialization.
45
- This method should handle converting nested Serializable objects and callables.
46
- """
47
- pass
48
-
49
- @classmethod
50
- @abstractmethod
51
- def from_dict(cls: type[T], data: dict[str, Any]) -> T:
52
- """Create instance from a dictionary representation.
53
- This method should handle reconstructing nested Serializable objects and callables.
54
- """
55
- pass
56
-
57
- # --- JSON Methods ---
58
- def to_json(self, indent: int | None = 2) -> str:
59
- """Serialize to JSON string."""
60
- # Import encoder locally to avoid making it a hard dependency if JSON isn't used
61
- from .json_encoder import FlockJSONEncoder
62
-
63
- try:
64
- # Note: to_dict should ideally prepare the structure fully.
65
- # FlockJSONEncoder is a fallback for types missed by to_dict.
66
- return json.dumps(
67
- self.to_dict(), cls=FlockJSONEncoder, indent=indent
68
- )
69
- except Exception as e:
70
- raise RuntimeError(
71
- f"Failed to serialize {self.__class__.__name__} to JSON: {e}"
72
- ) from e
73
-
74
- @classmethod
75
- def from_json(cls: type[T], json_str: str) -> T:
76
- """Create instance from JSON string."""
77
- try:
78
- data = json.loads(json_str)
79
- return cls.from_dict(data)
80
- except json.JSONDecodeError as e:
81
- raise ValueError(f"Invalid JSON string: {e}") from e
82
- except Exception as e:
83
- raise RuntimeError(
84
- f"Failed to deserialize {cls.__name__} from JSON: {e}"
85
- ) from e
86
-
87
- # --- YAML Methods ---
88
- def to_yaml(
89
- self,
90
- path_type: Literal["absolute", "relative"] = "relative",
91
- sort_keys=False,
92
- default_flow_style=False,
93
- ) -> str:
94
- """Serialize to YAML string.
95
-
96
- Args:
97
- path_type: How file paths should be formatted ('absolute' or 'relative')
98
- sort_keys: Whether to sort dictionary keys
99
- default_flow_style: YAML flow style setting
100
- """
101
- if not YAML_AVAILABLE:
102
- raise NotImplementedError(
103
- "YAML support requires PyYAML: pip install pyyaml"
104
- )
105
- try:
106
- # If to_dict supports path_type, pass it; otherwise use standard to_dict
107
- if "path_type" in self.to_dict.__code__.co_varnames:
108
- dict_data = self.to_dict(path_type=path_type)
109
- else:
110
- dict_data = self.to_dict()
111
-
112
- return yaml.dump(
113
- dict_data,
114
- sort_keys=sort_keys,
115
- default_flow_style=default_flow_style,
116
- allow_unicode=True,
117
- )
118
- except Exception as e:
119
- raise RuntimeError(
120
- f"Failed to serialize {self.__class__.__name__} to YAML: {e}"
121
- ) from e
122
-
123
- @classmethod
124
- def from_yaml(cls: type[T], yaml_str: str) -> T:
125
- """Create instance from YAML string."""
126
- if not YAML_AVAILABLE:
127
- raise NotImplementedError(
128
- "YAML support requires PyYAML: pip install pyyaml"
129
- )
130
- try:
131
- data = yaml.safe_load(yaml_str)
132
- if not isinstance(data, dict):
133
- raise TypeError(
134
- f"YAML did not yield a dictionary for {cls.__name__}"
135
- )
136
- return cls.from_dict(data)
137
- except yaml.YAMLError as e:
138
- raise ValueError(f"Invalid YAML string: {e}") from e
139
- except Exception as e:
140
- raise RuntimeError(
141
- f"Failed to deserialize {cls.__name__} from YAML: {e}"
142
- ) from e
143
-
144
- def to_yaml_file(
145
- self,
146
- path: Path | str,
147
- path_type: Literal["absolute", "relative"] = "relative",
148
- **yaml_dump_kwargs,
149
- ) -> None:
150
- """Serialize to YAML file.
151
-
152
- Args:
153
- path: File path to write to
154
- path_type: How file paths should be formatted ('absolute' or 'relative')
155
- **yaml_dump_kwargs: Additional arguments to pass to yaml.dump
156
- """
157
- if not YAML_AVAILABLE:
158
- raise NotImplementedError(
159
- "YAML support requires PyYAML: pip install pyyaml"
160
- )
161
- path = Path(path)
162
- try:
163
- path.parent.mkdir(parents=True, exist_ok=True)
164
- yaml_str = self.to_yaml(path_type=path_type, **yaml_dump_kwargs)
165
- path.write_text(yaml_str, encoding="utf-8")
166
- except Exception as e:
167
- raise RuntimeError(
168
- f"Failed to write {self.__class__.__name__} to YAML file {path}: {e}"
169
- ) from e
170
-
171
- @classmethod
172
- def from_yaml_file(cls: type[T], path: Path | str) -> T:
173
- """Create instance from YAML file."""
174
- if not YAML_AVAILABLE:
175
- raise NotImplementedError(
176
- "YAML support requires PyYAML: pip install pyyaml"
177
- )
178
- path = Path(path)
179
- try:
180
- yaml_str = path.read_text(encoding="utf-8")
181
- return cls.from_yaml(yaml_str)
182
- except FileNotFoundError:
183
- raise
184
- except Exception as e:
185
- raise RuntimeError(
186
- f"Failed to read {cls.__name__} from YAML file {path}: {e}"
187
- ) from e
188
-
189
- # --- MsgPack Methods ---
190
- def to_msgpack(self) -> bytes:
191
- """Serialize to msgpack bytes."""
192
- if not MSGPACK_AVAILABLE:
193
- raise NotImplementedError(
194
- "MsgPack support requires msgpack: pip install msgpack"
195
- )
196
- try:
197
- # Use default hook for complex types if needed, or rely on to_dict
198
- return msgpack.packb(self.to_dict(), use_bin_type=True)
199
- except Exception as e:
200
- raise RuntimeError(
201
- f"Failed to serialize {self.__class__.__name__} to MsgPack: {e}"
202
- ) from e
203
-
204
- @classmethod
205
- def from_msgpack(cls: type[T], msgpack_bytes: bytes) -> T:
206
- """Create instance from msgpack bytes."""
207
- if not MSGPACK_AVAILABLE:
208
- raise NotImplementedError(
209
- "MsgPack support requires msgpack: pip install msgpack"
210
- )
211
- try:
212
- # Use object_hook if custom deserialization is needed beyond from_dict
213
- data = msgpack.unpackb(msgpack_bytes, raw=False)
214
- if not isinstance(data, dict):
215
- raise TypeError(
216
- f"MsgPack did not yield a dictionary for {cls.__name__}"
217
- )
218
- return cls.from_dict(data)
219
- except Exception as e:
220
- raise RuntimeError(
221
- f"Failed to deserialize {cls.__name__} from MsgPack: {e}"
222
- ) from e
223
-
224
- def to_msgpack_file(self, path: Path | str) -> None:
225
- """Serialize to msgpack file."""
226
- if not MSGPACK_AVAILABLE:
227
- raise NotImplementedError(
228
- "MsgPack support requires msgpack: pip install msgpack"
229
- )
230
- path = Path(path)
231
- try:
232
- path.parent.mkdir(parents=True, exist_ok=True)
233
- msgpack_bytes = self.to_msgpack()
234
- path.write_bytes(msgpack_bytes)
235
- except Exception as e:
236
- raise RuntimeError(
237
- f"Failed to write {self.__class__.__name__} to MsgPack file {path}: {e}"
238
- ) from e
239
-
240
- @classmethod
241
- def from_msgpack_file(cls: type[T], path: Path | str) -> T:
242
- """Create instance from msgpack file."""
243
- if not MSGPACK_AVAILABLE:
244
- raise NotImplementedError(
245
- "MsgPack support requires msgpack: pip install msgpack"
246
- )
247
- path = Path(path)
248
- try:
249
- msgpack_bytes = path.read_bytes()
250
- return cls.from_msgpack(msgpack_bytes)
251
- except FileNotFoundError:
252
- raise
253
- except Exception as e:
254
- raise RuntimeError(
255
- f"Failed to read {cls.__name__} from MsgPack file {path}: {e}"
256
- ) from e
257
-
258
- # --- Pickle Methods (Use with caution due to security risks) ---
259
- def to_pickle(self) -> bytes:
260
- """Serialize to pickle bytes using cloudpickle."""
261
- if not PICKLE_AVAILABLE:
262
- raise NotImplementedError(
263
- "Pickle support requires cloudpickle: pip install cloudpickle"
264
- )
265
- try:
266
- return cloudpickle.dumps(self)
267
- except Exception as e:
268
- raise RuntimeError(
269
- f"Failed to serialize {self.__class__.__name__} to Pickle: {e}"
270
- ) from e
271
-
272
- @classmethod
273
- def from_pickle(cls: type[T], pickle_bytes: bytes) -> T:
274
- """Create instance from pickle bytes using cloudpickle."""
275
- if not PICKLE_AVAILABLE:
276
- raise NotImplementedError(
277
- "Pickle support requires cloudpickle: pip install cloudpickle"
278
- )
279
- try:
280
- instance = cloudpickle.loads(pickle_bytes)
281
- if not isinstance(instance, cls):
282
- raise TypeError(
283
- f"Deserialized object is not of type {cls.__name__}"
284
- )
285
- return instance
286
- except Exception as e:
287
- raise RuntimeError(
288
- f"Failed to deserialize {cls.__name__} from Pickle: {e}"
289
- ) from e
290
-
291
- def to_pickle_file(self, path: Path | str) -> None:
292
- """Serialize to pickle file using cloudpickle."""
293
- if not PICKLE_AVAILABLE:
294
- raise NotImplementedError(
295
- "Pickle support requires cloudpickle: pip install cloudpickle"
296
- )
297
- path = Path(path)
298
- try:
299
- path.parent.mkdir(parents=True, exist_ok=True)
300
- pickle_bytes = self.to_pickle()
301
- path.write_bytes(pickle_bytes)
302
- except Exception as e:
303
- raise RuntimeError(
304
- f"Failed to write {self.__class__.__name__} to Pickle file {path}: {e}"
305
- ) from e
306
-
307
- @classmethod
308
- def from_pickle_file(cls: type[T], path: Path | str) -> T:
309
- """Create instance from pickle file using cloudpickle."""
310
- if not PICKLE_AVAILABLE:
311
- raise NotImplementedError(
312
- "Pickle support requires cloudpickle: pip install cloudpickle"
313
- )
314
- path = Path(path)
315
- try:
316
- pickle_bytes = path.read_bytes()
317
- return cls.from_pickle(pickle_bytes)
318
- except FileNotFoundError:
319
- raise
320
- except Exception as e:
321
- raise RuntimeError(
322
- f"Failed to read {cls.__name__} from Pickle file {path}: {e}"
323
- ) from e
324
-
325
- # _filter_none_values remains unchanged
326
- @staticmethod
327
- def _filter_none_values(data: Any) -> Any:
328
- """Filter out None values from dictionaries and lists recursively."""
329
- if isinstance(data, dict):
330
- return {
331
- k: Serializable._filter_none_values(v)
332
- for k, v in data.items()
333
- if v is not None
334
- }
335
- elif isinstance(data, list):
336
- # Filter None from list items AND recursively filter within items
337
- return [
338
- Serializable._filter_none_values(item)
339
- for item in data
340
- if item is not None
341
- ]
342
- return data