flock-core 0.5.0b28__py3-none-any.whl → 0.5.56b0__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 (359) 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.56b0.dist-info/METADATA +747 -0
  162. flock_core-0.5.56b0.dist-info/RECORD +398 -0
  163. flock_core-0.5.56b0.dist-info/entry_points.txt +2 -0
  164. {flock_core-0.5.0b28.dist-info → flock_core-0.5.56b0.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 -22
  197. flock/components/utility/example_utility_component.py +0 -250
  198. flock/components/utility/feedback_utility_component.py +0 -206
  199. flock/components/utility/memory_utility_component.py +0 -550
  200. flock/components/utility/metrics_utility_component.py +0 -700
  201. flock/config.py +0 -61
  202. flock/core/__init__.py +0 -110
  203. flock/core/agent/__init__.py +0 -16
  204. flock/core/agent/default_agent.py +0 -216
  205. flock/core/agent/flock_agent_components.py +0 -104
  206. flock/core/agent/flock_agent_execution.py +0 -101
  207. flock/core/agent/flock_agent_integration.py +0 -260
  208. flock/core/agent/flock_agent_lifecycle.py +0 -186
  209. flock/core/agent/flock_agent_serialization.py +0 -381
  210. flock/core/api/__init__.py +0 -10
  211. flock/core/api/custom_endpoint.py +0 -45
  212. flock/core/api/endpoints.py +0 -254
  213. flock/core/api/main.py +0 -162
  214. flock/core/api/models.py +0 -97
  215. flock/core/api/run_store.py +0 -224
  216. flock/core/api/runner.py +0 -44
  217. flock/core/api/service.py +0 -214
  218. flock/core/component/__init__.py +0 -15
  219. flock/core/component/agent_component_base.py +0 -309
  220. flock/core/component/evaluation_component.py +0 -62
  221. flock/core/component/routing_component.py +0 -74
  222. flock/core/component/utility_component.py +0 -69
  223. flock/core/config/flock_agent_config.py +0 -58
  224. flock/core/config/scheduled_agent_config.py +0 -40
  225. flock/core/context/context.py +0 -213
  226. flock/core/context/context_manager.py +0 -37
  227. flock/core/context/context_vars.py +0 -10
  228. flock/core/evaluation/utils.py +0 -396
  229. flock/core/execution/batch_executor.py +0 -369
  230. flock/core/execution/evaluation_executor.py +0 -438
  231. flock/core/execution/local_executor.py +0 -31
  232. flock/core/execution/opik_executor.py +0 -103
  233. flock/core/execution/temporal_executor.py +0 -164
  234. flock/core/flock.py +0 -634
  235. flock/core/flock_agent.py +0 -336
  236. flock/core/flock_factory.py +0 -613
  237. flock/core/flock_scheduler.py +0 -166
  238. flock/core/flock_server_manager.py +0 -136
  239. flock/core/interpreter/python_interpreter.py +0 -689
  240. flock/core/mcp/__init__.py +0 -1
  241. flock/core/mcp/flock_mcp_server.py +0 -680
  242. flock/core/mcp/mcp_client_manager.py +0 -201
  243. flock/core/mcp/types/__init__.py +0 -1
  244. flock/core/mixin/dspy_integration.py +0 -403
  245. flock/core/mixin/prompt_parser.py +0 -125
  246. flock/core/orchestration/__init__.py +0 -15
  247. flock/core/orchestration/flock_batch_processor.py +0 -94
  248. flock/core/orchestration/flock_evaluator.py +0 -113
  249. flock/core/orchestration/flock_execution.py +0 -295
  250. flock/core/orchestration/flock_initialization.py +0 -149
  251. flock/core/orchestration/flock_server_manager.py +0 -67
  252. flock/core/orchestration/flock_web_server.py +0 -117
  253. flock/core/registry/__init__.py +0 -45
  254. flock/core/registry/agent_registry.py +0 -69
  255. flock/core/registry/callable_registry.py +0 -139
  256. flock/core/registry/component_discovery.py +0 -142
  257. flock/core/registry/component_registry.py +0 -64
  258. flock/core/registry/config_mapping.py +0 -64
  259. flock/core/registry/decorators.py +0 -137
  260. flock/core/registry/registry_hub.py +0 -205
  261. flock/core/registry/server_registry.py +0 -57
  262. flock/core/registry/type_registry.py +0 -86
  263. flock/core/serialization/__init__.py +0 -13
  264. flock/core/serialization/callable_registry.py +0 -52
  265. flock/core/serialization/flock_serializer.py +0 -832
  266. flock/core/serialization/json_encoder.py +0 -41
  267. flock/core/serialization/secure_serializer.py +0 -175
  268. flock/core/serialization/serializable.py +0 -342
  269. flock/core/serialization/serialization_utils.py +0 -412
  270. flock/core/util/file_path_utils.py +0 -223
  271. flock/core/util/hydrator.py +0 -309
  272. flock/core/util/input_resolver.py +0 -164
  273. flock/core/util/loader.py +0 -59
  274. flock/core/util/splitter.py +0 -219
  275. flock/di.py +0 -27
  276. flock/platform/docker_tools.py +0 -49
  277. flock/platform/jaeger_install.py +0 -86
  278. flock/webapp/__init__.py +0 -1
  279. flock/webapp/app/__init__.py +0 -0
  280. flock/webapp/app/api/__init__.py +0 -0
  281. flock/webapp/app/api/agent_management.py +0 -241
  282. flock/webapp/app/api/execution.py +0 -709
  283. flock/webapp/app/api/flock_management.py +0 -129
  284. flock/webapp/app/api/registry_viewer.py +0 -30
  285. flock/webapp/app/chat.py +0 -665
  286. flock/webapp/app/config.py +0 -104
  287. flock/webapp/app/dependencies.py +0 -117
  288. flock/webapp/app/main.py +0 -1070
  289. flock/webapp/app/middleware.py +0 -113
  290. flock/webapp/app/models_ui.py +0 -7
  291. flock/webapp/app/services/__init__.py +0 -0
  292. flock/webapp/app/services/feedback_file_service.py +0 -363
  293. flock/webapp/app/services/flock_service.py +0 -337
  294. flock/webapp/app/services/sharing_models.py +0 -81
  295. flock/webapp/app/services/sharing_store.py +0 -762
  296. flock/webapp/app/templates/theme_mapper.html +0 -326
  297. flock/webapp/app/theme_mapper.py +0 -812
  298. flock/webapp/app/utils.py +0 -85
  299. flock/webapp/run.py +0 -215
  300. flock/webapp/static/css/chat.css +0 -301
  301. flock/webapp/static/css/components.css +0 -167
  302. flock/webapp/static/css/header.css +0 -39
  303. flock/webapp/static/css/layout.css +0 -46
  304. flock/webapp/static/css/sidebar.css +0 -127
  305. flock/webapp/static/css/two-pane.css +0 -48
  306. flock/webapp/templates/base.html +0 -200
  307. flock/webapp/templates/chat.html +0 -152
  308. flock/webapp/templates/chat_settings.html +0 -19
  309. flock/webapp/templates/flock_editor.html +0 -16
  310. flock/webapp/templates/index.html +0 -12
  311. flock/webapp/templates/partials/_agent_detail_form.html +0 -93
  312. flock/webapp/templates/partials/_agent_list.html +0 -18
  313. flock/webapp/templates/partials/_agent_manager_view.html +0 -51
  314. flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
  315. flock/webapp/templates/partials/_chat_container.html +0 -15
  316. flock/webapp/templates/partials/_chat_messages.html +0 -57
  317. flock/webapp/templates/partials/_chat_settings_form.html +0 -85
  318. flock/webapp/templates/partials/_create_flock_form.html +0 -50
  319. flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
  320. flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
  321. flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
  322. flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
  323. flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
  324. flock/webapp/templates/partials/_env_vars_table.html +0 -23
  325. flock/webapp/templates/partials/_execution_form.html +0 -118
  326. flock/webapp/templates/partials/_execution_view_container.html +0 -28
  327. flock/webapp/templates/partials/_flock_file_list.html +0 -23
  328. flock/webapp/templates/partials/_flock_properties_form.html +0 -52
  329. flock/webapp/templates/partials/_flock_upload_form.html +0 -16
  330. flock/webapp/templates/partials/_header_flock_status.html +0 -5
  331. flock/webapp/templates/partials/_load_manager_view.html +0 -49
  332. flock/webapp/templates/partials/_registry_table.html +0 -25
  333. flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
  334. flock/webapp/templates/partials/_results_display.html +0 -78
  335. flock/webapp/templates/partials/_settings_env_content.html +0 -9
  336. flock/webapp/templates/partials/_settings_theme_content.html +0 -14
  337. flock/webapp/templates/partials/_settings_view.html +0 -36
  338. flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
  339. flock/webapp/templates/partials/_share_link_snippet.html +0 -35
  340. flock/webapp/templates/partials/_sidebar.html +0 -74
  341. flock/webapp/templates/partials/_streaming_results_container.html +0 -195
  342. flock/webapp/templates/partials/_structured_data_view.html +0 -40
  343. flock/webapp/templates/partials/_theme_preview.html +0 -36
  344. flock/webapp/templates/registry_viewer.html +0 -84
  345. flock/webapp/templates/shared_run_page.html +0 -140
  346. flock/workflow/__init__.py +0 -0
  347. flock/workflow/activities.py +0 -196
  348. flock/workflow/agent_activities.py +0 -24
  349. flock/workflow/agent_execution_activity.py +0 -202
  350. flock/workflow/flock_workflow.py +0 -214
  351. flock/workflow/temporal_config.py +0 -96
  352. flock/workflow/temporal_setup.py +0 -68
  353. flock_core-0.5.0b28.dist-info/METADATA +0 -274
  354. flock_core-0.5.0b28.dist-info/RECORD +0 -561
  355. flock_core-0.5.0b28.dist-info/entry_points.txt +0 -2
  356. /flock/{core/logging → logging}/formatters/themes.py +0 -0
  357. /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
  358. /flock/{core/mcp → mcp}/util/__init__.py +0 -0
  359. {flock_core-0.5.0b28.dist-info → flock_core-0.5.56b0.dist-info}/WHEEL +0 -0
@@ -1,412 +0,0 @@
1
- # src/flock/core/serialization/serialization_utils.py
2
- """Utilities for recursive serialization/deserialization with callable handling."""
3
-
4
- import ast
5
- import builtins
6
- import importlib
7
- import sys
8
- import types
9
- import typing
10
- from collections.abc import Mapping, Sequence
11
- from enum import Enum
12
- from typing import (
13
- TYPE_CHECKING,
14
- Any,
15
- Literal,
16
- Union,
17
- get_args,
18
- get_origin,
19
- )
20
-
21
- from pydantic import BaseModel
22
-
23
- # Use TYPE_CHECKING to avoid circular imports
24
- if TYPE_CHECKING:
25
- pass
26
-
27
- from flock.core.registry import get_registry
28
- from flock.core.logging.logging import get_logger
29
-
30
- logger = get_logger("serialization.utils")
31
-
32
- # Remove this line to avoid circular import at module level
33
- # registry = get_registry() # Get singleton instance
34
-
35
- # --- Serialization Helper ---
36
-
37
- # src/flock/util/hydrator.py (or import from serialization_utils)
38
-
39
-
40
- def _format_type_to_string(type_hint: type) -> str:
41
- """Converts a Python type object back into its string representation."""
42
- # This needs to handle various typing scenarios (List, Dict, Union, Optional, Literal, custom types)
43
- origin = typing.get_origin(type_hint)
44
- args = typing.get_args(type_hint)
45
-
46
- # Handle common cases first
47
- if origin is list or origin is list:
48
- if args:
49
- return f"list[{_format_type_to_string(args[0])}]"
50
- return "list[Any]" # Or just "list"
51
- elif origin is dict or origin is dict:
52
- if args and len(args) == 2:
53
- return f"dict[{_format_type_to_string(args[0])}, {_format_type_to_string(args[1])}]"
54
- return "dict[Any, Any]" # Or just "dict"
55
- elif origin is Union or origin is types.UnionType:
56
- # Handle Optional[T] as Union[T, NoneType]
57
- if len(args) == 2 and type(None) in args:
58
- inner_type = next(t for t in args if t is not type(None))
59
- return _format_type_to_string(inner_type)
60
- # return f"Optional[{_format_type_to_string(inner_type)}]"
61
- return (
62
- f"Union[{', '.join(_format_type_to_string(arg) for arg in args)}]"
63
- )
64
- elif origin is Literal:
65
- formatted_args = []
66
- for arg in args:
67
- if isinstance(arg, str):
68
- formatted_args.append(f"'{arg}'")
69
- else:
70
- formatted_args.append(str(arg))
71
- return f"Literal[{', '.join(formatted_args)}]"
72
- elif hasattr(
73
- type_hint, "__forward_arg__"
74
- ): # Handle ForwardRefs if necessary
75
- return type_hint.__forward_arg__
76
- elif hasattr(type_hint, "__name__"):
77
- # Handle custom types registered in registry (get preferred name)
78
- registry = get_registry()
79
- try:
80
- # Prefer explicit type registration names when available
81
- for name, reg_type in registry.types.get_all_types().items():
82
- if reg_type == type_hint:
83
- return name
84
- except Exception:
85
- # Defensive: if registry helper is unavailable, fall back below
86
- pass
87
- return type_hint.__name__ # Fallback to class name if not registered
88
- else:
89
- # Fallback for complex types or types not handled above
90
- type_repr = str(type_hint).replace("typing.", "") # Basic cleanup
91
- type_repr = str(type_hint).replace("| None", "")
92
- type_repr = type_repr.strip()
93
- logger.debug(
94
- f"Using fallback string representation for type: {type_repr}"
95
- )
96
- return type_repr
97
-
98
-
99
- def extract_identifiers_from_type_str(type_str: str) -> set[str]:
100
- """Extract all identifiers from a type annotation string using the AST."""
101
- tree = ast.parse(type_str, mode="eval")
102
- identifiers = set()
103
-
104
- class IdentifierVisitor(ast.NodeVisitor):
105
- def visit_Name(self, node):
106
- identifiers.add(node.id)
107
-
108
- def visit_Attribute(self, node):
109
- # Optionally support dotted names like mymodule.MyModel
110
- full_name = []
111
- while isinstance(node, ast.Attribute):
112
- full_name.append(node.attr)
113
- node = node.value
114
- if isinstance(node, ast.Name):
115
- full_name.append(node.id)
116
- identifiers.add(".".join(reversed(full_name)))
117
-
118
- IdentifierVisitor().visit(tree)
119
- return identifiers
120
-
121
-
122
- def resolve_name(name: str):
123
- """Resolve a name to a Python object from loaded modules."""
124
- # Try dotted names first
125
- parts = name.split(".")
126
- obj = None
127
-
128
- if len(parts) == 1:
129
- # Search globals and builtins
130
- if parts[0] in globals():
131
- return globals()[parts[0]]
132
- if parts[0] in builtins.__dict__:
133
- return builtins.__dict__[parts[0]]
134
- else:
135
- try:
136
- obj = sys.modules[parts[0]]
137
- for part in parts[1:]:
138
- obj = getattr(obj, part)
139
- return obj
140
- except Exception:
141
- return None
142
-
143
- # Try all loaded modules' symbols
144
- for module in list(sys.modules.values()):
145
- if module is None or not hasattr(module, "__dict__"):
146
- continue
147
- if parts[0] in module.__dict__:
148
- return module.__dict__[parts[0]]
149
-
150
- return None
151
-
152
-
153
- def extract_pydantic_models_from_type_string(
154
- type_str: str,
155
- ) -> list[type[BaseModel]]:
156
- identifiers = extract_identifiers_from_type_str(type_str)
157
- models = []
158
- for name in identifiers:
159
- resolved = resolve_name(name)
160
- if (
161
- isinstance(resolved, type)
162
- and issubclass(resolved, BaseModel)
163
- and resolved is not BaseModel
164
- ):
165
- models.append(resolved)
166
- return models
167
-
168
-
169
- def collect_pydantic_models(
170
- type_hint, seen: set[type[BaseModel]] | None = None
171
- ) -> set[type[BaseModel]]:
172
- if seen is None:
173
- seen = set()
174
-
175
- origin = get_origin(type_hint)
176
- args = get_args(type_hint)
177
-
178
- # Direct BaseModel
179
- if isinstance(type_hint, type) and issubclass(type_hint, BaseModel):
180
- seen.add(type_hint)
181
- return seen
182
-
183
- # For Unions, Lists, Dicts, Tuples, etc.
184
- if origin is not None:
185
- for arg in args:
186
- collect_pydantic_models(arg, seen)
187
-
188
- return seen
189
-
190
-
191
- def serialize_item(item: Any) -> Any:
192
- """Recursively prepares an item for serialization (e.g., to dict for YAML/JSON).
193
- Converts known callables to their path strings using registry.
194
- Converts Pydantic models using model_dump.
195
- """
196
- # Import the registry lazily when needed
197
- from flock.core.registry import get_registry
198
-
199
- registry = get_registry()
200
-
201
- if callable(item) and not isinstance(item, type):
202
- path_str = registry.get_callable_path_string(
203
- item
204
- ) # Use registry helper
205
- if path_str:
206
- # Store the simple name (last part of the path) for cleaner YAML/JSON
207
- simple_name = path_str.split(".")[-1]
208
- logger.debug(
209
- f"Serializing callable '{getattr(item, '__name__', 'unknown')}' as reference: '{simple_name}' (from path '{path_str}')"
210
- )
211
- return {
212
- "__callable_ref__": simple_name
213
- } # Use simple name convention
214
- else:
215
- # Handle unregistered callables (e.g., lambdas defined inline)
216
- # Option 1: Raise error (stricter)
217
- # raise ValueError(f"Cannot serialize unregistered callable: {getattr(item, '__name__', item)}")
218
- # Option 2: Store string representation with warning (more lenient)
219
- logger.warning(
220
- f"Cannot serialize unregistered callable {getattr(item, '__name__', item)}, storing as string."
221
- )
222
- return str(item)
223
- elif isinstance(item, BaseModel):
224
- logger.debug(
225
- f"Serializing Pydantic model instance: {item.__class__.__name__}"
226
- )
227
- serialized_dict = {}
228
- # Iterate through defined fields in the model
229
- fields_to_iterate = {}
230
- if hasattr(item, "model_fields"): # Pydantic v2
231
- fields_to_iterate = item.model_fields
232
-
233
- for field_name in fields_to_iterate:
234
- # Get the value *from the instance*
235
- try:
236
- value = getattr(item, field_name)
237
- if value is not None: # Exclude None values
238
- # Recursively serialize the field's value
239
- serialized_dict[field_name] = serialize_item(value)
240
- except AttributeError:
241
- # Should not happen if iterating model_fields/__fields__ but handle defensively
242
- logger.warning(
243
- f"Attribute '{field_name}' not found on instance of {item.__class__.__name__} during serialization."
244
- )
245
- return serialized_dict
246
- elif isinstance(item, Mapping):
247
- return {key: serialize_item(value) for key, value in item.items()}
248
- elif isinstance(item, Sequence) and not isinstance(item, str):
249
- return [serialize_item(sub_item) for sub_item in item]
250
- elif isinstance(item, type): # Handle type objects themselves (e.g. if stored directly)
251
- # Prefer registered component/type names when possible
252
- type_name = registry.get_component_type_name(item)
253
- if type_name:
254
- return {"__component_ref__": type_name}
255
- # Fall back to module-qualified path for general types
256
- try:
257
- module = getattr(item, "__module__", None)
258
- name = getattr(item, "__name__", None)
259
- if module and name:
260
- return {"__type_ref__": f"{module}.{name}"}
261
- except Exception:
262
- pass
263
- logger.warning(f"Could not serialize type object {item}, storing as string.")
264
- return str(item)
265
- elif isinstance(item, Enum):
266
- return item.value
267
- else:
268
- # Return basic types as is
269
- return item
270
-
271
-
272
- # --- Deserialization Helper ---
273
-
274
-
275
- def deserialize_item(item: Any) -> Any:
276
- """Recursively processes a deserialized item (e.g., from YAML/JSON dict).
277
- Converts reference dicts back to actual callables or types using registry.
278
- Handles nested lists and dicts.
279
- """
280
- # Import the registry lazily when needed
281
- from flock.core.registry import get_registry
282
-
283
- registry = get_registry()
284
-
285
- if isinstance(item, Mapping):
286
- if "__callable_ref__" in item and len(item) == 1:
287
- ref_name = item["__callable_ref__"]
288
- try:
289
- # The registry's get_callable needs to handle lookup by simple name OR full path
290
- # Or we assume get_callable handles finding the right function from the simple name
291
- resolved_callable = registry.get_callable(ref_name)
292
- logger.debug(
293
- f"Deserialized callable reference '{ref_name}' to {resolved_callable}"
294
- )
295
- return resolved_callable
296
- except KeyError:
297
- logger.error(
298
- f"Callable reference '{ref_name}' not found during deserialization."
299
- )
300
- return None # Or raise?
301
- except Exception as e:
302
- logger.error(
303
- f"Error resolving callable reference '{ref_name}': {e}",
304
- exc_info=True,
305
- )
306
- return None
307
- elif "__component_ref__" in item and len(item) == 1:
308
- type_name = item["__component_ref__"]
309
- try:
310
- return registry.get_component(type_name)
311
- except KeyError:
312
- logger.error(
313
- f"Component reference '{type_name}' not found during deserialization."
314
- )
315
- return None
316
- elif "__type_ref__" in item and len(item) == 1:
317
- type_name = item["__type_ref__"]
318
- try:
319
- # For general types, use get_type or fallback to dynamic import like get_callable
320
- # Using get_type for now, assuming it needs registration
321
- return registry.get_type(type_name)
322
- except KeyError:
323
- # Attempt dynamic import as fallback if get_type fails (similar to get_callable)
324
- try:
325
- if "." not in type_name: # Builtins?
326
- mod = importlib.import_module("builtins")
327
- else:
328
- module_name, class_name = type_name.rsplit(".", 1)
329
- mod = importlib.import_module(module_name)
330
- type_obj = getattr(mod, class_name)
331
- if isinstance(type_obj, type):
332
- registry.register_type(
333
- type_obj, type_name
334
- ) # Cache it
335
- return type_obj
336
- else:
337
- raise TypeError()
338
- except Exception:
339
- logger.error(
340
- f"Type reference '{type_name}' not found in registry or via dynamic import."
341
- )
342
- return None
343
-
344
- else:
345
- # Recursively deserialize dictionary values
346
- return {key: deserialize_item(value) for key, value in item.items()}
347
- elif isinstance(item, Sequence) and not isinstance(item, str):
348
- return [deserialize_item(sub_item) for sub_item in item]
349
- else:
350
- # Return basic types as is
351
- return item
352
-
353
-
354
- # --- Component Deserialization Helper ---
355
- def deserialize_component(
356
- data: dict | None, expected_base_type: type
357
- ) -> Any | None:
358
- """Deserializes a component (Module, Evaluator, Router) from its dict representation.
359
- Uses the 'type' field to find the correct class via registry.
360
- """
361
- # Import the registry and COMPONENT_BASE_TYPES lazily when needed
362
- from flock.core.registry import get_registry
363
-
364
- registry = get_registry()
365
-
366
- if data is None:
367
- return None
368
- if not isinstance(data, dict):
369
- logger.error(
370
- f"Expected dict for component deserialization, got {type(data)}"
371
- )
372
- return None
373
-
374
- type_name = data.get(
375
- "type"
376
- ) # Assuming 'type' key holds the class name string
377
- if not type_name:
378
- logger.error(f"Component data missing 'type' field: {data}")
379
- return None
380
-
381
- try:
382
- ComponentClass = registry.get_component(type_name) # Use registry
383
- # Optional: Keep the base type check
384
- try:
385
- from flock.core.component.agent_component_base import AgentComponent
386
- if not issubclass(ComponentClass, AgentComponent):
387
- logger.warning(
388
- f"Deserialized class '{type_name}' is not a subclass of AgentComponent."
389
- )
390
- except ImportError:
391
- # AgentComponent not available during setup
392
- pass
393
-
394
- # Recursively deserialize the data *before* passing to Pydantic constructor
395
- # This handles nested callables/types within the component's config/data
396
- deserialized_data_for_init = {}
397
- for k, v in data.items():
398
- # Don't pass the 'type' field itself to the constructor if it matches class name
399
- if k == "type" and v == ComponentClass.__name__:
400
- continue
401
- deserialized_data_for_init[k] = deserialize_item(v)
402
-
403
- # Use Pydantic constructor directly. Assumes keys match field names.
404
- # from_dict could be added to components for more complex logic if needed.
405
- return ComponentClass(**deserialized_data_for_init)
406
-
407
- except (KeyError, TypeError, Exception) as e:
408
- logger.error(
409
- f"Failed to deserialize component of type '{type_name}': {e}",
410
- exc_info=True,
411
- )
412
- return None
@@ -1,223 +0,0 @@
1
- """Utility functions for handling file paths in Flock.
2
-
3
- This module provides utilities for working with file paths,
4
- especially for components that may be loaded from file system paths
5
- rather than module imports.
6
- """
7
-
8
- import importlib.util
9
- import inspect
10
- import os
11
- import sys
12
- from pathlib import Path
13
- from typing import Any
14
-
15
-
16
- def get_file_path(obj: Any) -> str | None:
17
- """Get the file path for a Python object.
18
-
19
- Args:
20
- obj: The object to get the file path for
21
-
22
- Returns:
23
- The file path if it can be determined, None otherwise
24
- """
25
- try:
26
- if inspect.ismodule(obj):
27
- return obj.__file__
28
- elif inspect.isclass(obj) or inspect.isfunction(obj):
29
- return inspect.getfile(obj)
30
- return None
31
- except (TypeError, ValueError):
32
- return None
33
-
34
-
35
- def normalize_path(path: str) -> str:
36
- """Normalize a path for consistent representation.
37
-
38
- Args:
39
- path: The path to normalize
40
-
41
- Returns:
42
- The normalized path
43
- """
44
- return os.path.normpath(path)
45
-
46
-
47
- def is_same_path(path1: str, path2: str) -> bool:
48
- """Check if two paths point to the same file.
49
-
50
- Args:
51
- path1: The first path
52
- path2: The second path
53
-
54
- Returns:
55
- True if the paths point to the same file, False otherwise
56
- """
57
- return os.path.normpath(os.path.abspath(path1)) == os.path.normpath(
58
- os.path.abspath(path2)
59
- )
60
-
61
-
62
- def get_relative_path(path: str, base_path: str | None = None) -> str:
63
- """Get a path relative to a base path.
64
-
65
- Args:
66
- path: The path to make relative
67
- base_path: The base path (defaults to current working directory)
68
-
69
- Returns:
70
- The relative path
71
- """
72
- if base_path is None:
73
- base_path = os.getcwd()
74
-
75
- return os.path.relpath(path, base_path)
76
-
77
-
78
- def load_class_from_file(file_path: str, class_name: str) -> type | None:
79
- """Load a class from a file.
80
-
81
- Args:
82
- file_path: The path to the file
83
- class_name: The name of the class to load
84
-
85
- Returns:
86
- The loaded class, or None if it could not be loaded
87
- """
88
- try:
89
- # Generate a unique module name to avoid conflicts
90
- module_name = f"flock_dynamic_import_{hash(file_path)}"
91
-
92
- # Create a spec for the module
93
- spec = importlib.util.spec_from_file_location(module_name, file_path)
94
- if not spec or not spec.loader:
95
- return None
96
-
97
- # Create and load the module
98
- module = importlib.util.module_from_spec(spec)
99
- sys.modules[module_name] = module
100
- spec.loader.exec_module(module)
101
-
102
- # Get the class from the module
103
- if not hasattr(module, class_name):
104
- return None
105
-
106
- return getattr(module, class_name)
107
- except Exception:
108
- return None
109
-
110
-
111
- def get_project_root() -> Path:
112
- """Get the project root directory.
113
-
114
- Returns:
115
- The project root path
116
- """
117
- # Try to find the directory containing pyproject.toml or setup.py
118
- current_dir = Path(os.getcwd())
119
-
120
- # Walk up the directory tree looking for project markers
121
- for path in [current_dir, *current_dir.parents]:
122
- if (path / "pyproject.toml").exists() or (path / "setup.py").exists():
123
- return path
124
-
125
- # Default to current directory if no project markers found
126
- return current_dir
127
-
128
-
129
- def component_path_to_file_path(component_path: str) -> str | None:
130
- """Convert a component path (module.ClassName) to a file path.
131
-
132
- Args:
133
- component_path: The component path in the form module.ClassName
134
-
135
- Returns:
136
- The file path if it can be determined, None otherwise
137
- """
138
- try:
139
- # Split into module path and class name
140
- if "." not in component_path:
141
- return None
142
-
143
- module_path, class_name = component_path.rsplit(".", 1)
144
-
145
- # Import the module
146
- module = importlib.import_module(module_path)
147
-
148
- # Get the file path
149
- if hasattr(module, "__file__"):
150
- return module.__file__
151
-
152
- return None
153
- except (ImportError, AttributeError):
154
- return None
155
-
156
-
157
- def file_path_to_component_path(file_path: str, class_name: str) -> str | None:
158
- """Convert a file path and class name to a component path (module.ClassName).
159
-
160
- This is approximate and may not work in all cases, especially for non-standard
161
- module structures.
162
-
163
- Args:
164
- file_path: The file path to the module
165
- class_name: The name of the class
166
-
167
- Returns:
168
- The component path if it can be determined, None otherwise
169
- """
170
- try:
171
- # Convert the file path to an absolute path
172
- abs_path = os.path.abspath(file_path)
173
-
174
- # Get the project root
175
- root = get_project_root()
176
-
177
- # Get the relative path from the project root
178
- rel_path = os.path.relpath(abs_path, root)
179
-
180
- # Convert to a module path
181
- module_path = os.path.splitext(rel_path)[0].replace(os.sep, ".")
182
-
183
- # Remove 'src.' prefix if present (common in Python projects)
184
- if module_path.startswith("src."):
185
- module_path = module_path[4:]
186
-
187
- # Combine with the class name
188
- return f"{module_path}.{class_name}"
189
- except Exception:
190
- return None
191
-
192
-
193
- def register_file_paths_in_registry(
194
- component_paths: dict[str, str], registry: Any | None = None
195
- ) -> bool:
196
- """Register file paths in the registry.
197
-
198
- Args:
199
- component_paths: Dictionary mapping component paths to file paths
200
- registry: The registry to register in (defaults to the global registry)
201
-
202
- Returns:
203
- True if all paths were registered, False otherwise
204
- """
205
- try:
206
- # Get the global registry if none provided
207
- if registry is None:
208
- from flock.core.flock_registry import get_registry
209
-
210
- registry = get_registry()
211
-
212
- # Initialize component_file_paths if needed
213
- if not hasattr(registry, "_component_file_paths"):
214
- registry._component_file_paths = {}
215
-
216
- # Register each path
217
- for component_name, file_path in component_paths.items():
218
- if component_name in registry._components:
219
- registry._component_file_paths[component_name] = file_path
220
-
221
- return True
222
- except Exception:
223
- return False