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,309 +0,0 @@
1
- # src/flock/core/util/hydrator.py (Revised - Simpler)
2
-
3
- import asyncio
4
- import json
5
- from typing import (
6
- Any,
7
- TypeVar,
8
- get_type_hints,
9
- )
10
-
11
- from pydantic import BaseModel
12
-
13
- # Import necessary Flock components
14
- from flock.core import Flock, FlockFactory
15
- from flock.core.logging.logging import get_logger
16
-
17
- # Import helper to format type hints back to strings
18
- from flock.core.serialization.serialization_utils import _format_type_to_string
19
-
20
- logger = get_logger("hydrator")
21
- T = TypeVar("T", bound=BaseModel)
22
-
23
-
24
- def flockclass(model: str = "openai/gpt-4o", agent_description: str | None = None):
25
- """Decorator to add a .hydrate() method to a Pydantic class.
26
- Leverages a dynamic Flock agent to fill missing (None) fields.
27
-
28
- Args:
29
- model: The default LLM model identifier to use for hydration.
30
- agent_description: An optional description for the dynamically created agent.
31
- """
32
-
33
- def decorator(cls: type[T]) -> type[T]:
34
- if not issubclass(cls, BaseModel):
35
- raise TypeError(
36
- "@flockclass can only decorate Pydantic BaseModel subclasses."
37
- )
38
-
39
- # Store metadata on the class
40
- setattr(cls, "__flock_model__", model)
41
- setattr(cls, "__flock_agent_description__", agent_description)
42
-
43
- # --- Attach the async hydrate method directly ---
44
- async def hydrate_async(self) -> T:
45
- """Hydrates the object by filling None fields using a dynamic Flock agent.
46
- Uses existing non-None fields as input context.
47
- Returns the hydrated object (self).
48
- """
49
- class_name = self.__class__.__name__
50
- logger.info(f"Starting hydration for instance of {class_name}")
51
-
52
- # Get field information
53
- all_fields, type_hints = _get_model_fields(self, class_name)
54
- if all_fields is None or type_hints is None:
55
- return self # Return early if field introspection failed
56
-
57
- # Identify existing and missing fields
58
- existing_data, missing_fields = _identify_fields(self, all_fields)
59
-
60
- if not missing_fields:
61
- logger.info(f"No fields to hydrate for {class_name} instance.")
62
- return self
63
-
64
- logger.debug(f"{class_name}: Fields to hydrate: {missing_fields}")
65
- logger.debug(
66
- f"{class_name}: Existing data for context: {json.dumps(existing_data, default=str)}"
67
- )
68
-
69
- # Create agent signatures
70
- input_str, output_str, input_parts = _build_agent_signatures(
71
- existing_data,
72
- missing_fields,
73
- type_hints,
74
- all_fields,
75
- class_name,
76
- )
77
-
78
- # Create and run agent
79
- result = await _run_hydration_agent(
80
- self,
81
- input_str,
82
- output_str,
83
- input_parts,
84
- existing_data,
85
- class_name,
86
- )
87
- if result is None:
88
- return self # Return early if agent run failed
89
-
90
- # Update object fields with results
91
- _update_fields_with_results(self, result, missing_fields, class_name)
92
-
93
- return self
94
-
95
- # --- Attach the sync hydrate method directly ---
96
- def hydrate(self) -> T:
97
- """Synchronous wrapper for the async hydrate method."""
98
- try:
99
- # Try to get the current running loop
100
- loop = asyncio.get_running_loop()
101
-
102
- # If we reach here, there is a running loop
103
- if loop.is_running():
104
- # This runs the coroutine in the existing loop from a different thread
105
- import concurrent.futures
106
-
107
- with concurrent.futures.ThreadPoolExecutor() as executor:
108
- future = executor.submit(asyncio.run, hydrate_async(self))
109
- return future.result()
110
- else:
111
- # There's a loop but it's not running
112
- return loop.run_until_complete(hydrate_async(self))
113
-
114
- except RuntimeError: # No running loop
115
- # If no loop is running, create a new one and run our coroutine
116
- return asyncio.run(hydrate_async(self))
117
-
118
- # Attach the methods to the class
119
- setattr(cls, "hydrate_async", hydrate_async)
120
- setattr(cls, "hydrate", hydrate)
121
- setattr(cls, "hydrate_sync", hydrate) # Alias for backward compatibility
122
-
123
- logger.debug(f"Attached hydrate methods to class {cls.__name__}")
124
- return cls
125
-
126
- return decorator
127
-
128
-
129
- def _get_model_fields(
130
- obj: BaseModel, class_name: str
131
- ) -> tuple[dict | None, dict | None]:
132
- """Extracts field information from a Pydantic model, handling v1/v2 compatibility."""
133
- try:
134
- if hasattr(obj, "model_fields"): # Pydantic v2
135
- all_fields = obj.model_fields
136
- type_hints = {name: field.annotation for name, field in all_fields.items()}
137
- else: # Pydantic v1 fallback
138
- type_hints = get_type_hints(obj.__class__)
139
- all_fields = getattr(obj, "__fields__", {name: None for name in type_hints})
140
- return all_fields, type_hints
141
- except Exception as e:
142
- logger.error(
143
- f"Could not get fields/type hints for {class_name}: {e}",
144
- exc_info=True,
145
- )
146
- return None, None
147
-
148
-
149
- def _identify_fields(
150
- obj: BaseModel, all_fields: dict
151
- ) -> tuple[dict[str, Any], list[str]]:
152
- """Identifies existing (non-None) and missing fields in the object."""
153
- existing_data: dict[str, Any] = {}
154
- missing_fields: list[str] = []
155
-
156
- for field_name in all_fields:
157
- if hasattr(obj, field_name): # Check if attribute exists
158
- value = getattr(obj, field_name)
159
- if value is not None:
160
- existing_data[field_name] = value
161
- else:
162
- missing_fields.append(field_name)
163
-
164
- return existing_data, missing_fields
165
-
166
-
167
- def _build_agent_signatures(
168
- existing_data: dict[str, Any],
169
- missing_fields: list[str],
170
- type_hints: dict,
171
- all_fields: dict,
172
- class_name: str,
173
- ) -> tuple[str, str, list]:
174
- """Builds input and output signatures for the dynamic agent."""
175
- # Input signature based on existing data
176
- input_parts = []
177
- for name in existing_data:
178
- field_type = type_hints.get(name, Any)
179
- type_str = _format_type_to_string(field_type)
180
- field_info = all_fields.get(name)
181
- field_desc = getattr(field_info, "description", "")
182
- if field_desc:
183
- input_parts.append(f"{name}: {type_str} | {field_desc}")
184
- else:
185
- input_parts.append(f"{name}: {type_str}")
186
-
187
- input_str = (
188
- ", ".join(input_parts)
189
- if input_parts
190
- else "context_info: dict | Optional context if no fields have values"
191
- )
192
-
193
- # Output signature based on missing fields
194
- output_parts = []
195
- for name in missing_fields:
196
- field_type = type_hints.get(name, Any)
197
- type_str = _format_type_to_string(field_type)
198
- field_info = all_fields.get(name)
199
- field_desc = getattr(field_info, "description", "")
200
- if field_desc:
201
- output_parts.append(f"{name}: {type_str} | {field_desc}")
202
- else:
203
- output_parts.append(f"{name}: {type_str}")
204
-
205
- output_str = ", ".join(output_parts)
206
-
207
- return input_str, output_str, input_parts
208
-
209
-
210
- async def _run_hydration_agent(
211
- obj: BaseModel,
212
- input_str: str,
213
- output_str: str,
214
- input_parts: list,
215
- existing_data: dict[str, Any],
216
- class_name: str,
217
- ) -> dict[str, Any] | None:
218
- """Creates and runs a dynamic Flock agent to hydrate the object."""
219
- # Agent configuration
220
- agent_name = f"hydrator_{class_name}_{id(obj)}"
221
- description = (
222
- getattr(obj, "__flock_agent_description__", None)
223
- or f"Agent that completes missing data for a {class_name} object."
224
- )
225
- hydration_model = getattr(obj, "__flock_model__", "openai/gpt-4o")
226
-
227
- logger.debug(f"Creating dynamic agent '{agent_name}' for {class_name}")
228
- logger.debug(f" Input Schema: {input_str}")
229
- logger.debug(f" Output Schema: {output_str}")
230
-
231
- try:
232
- # Create agent
233
- dynamic_agent = FlockFactory.create_default_agent(
234
- name=agent_name,
235
- description=description,
236
- input=input_str,
237
- output=output_str,
238
- model=hydration_model,
239
- no_output=True,
240
- use_cache=False,
241
- )
242
-
243
- # Create temporary Flock
244
- temp_flock = Flock(
245
- name=f"temp_hydrator_flock_{agent_name}",
246
- model=hydration_model,
247
- show_flock_banner=False,
248
- )
249
- temp_flock.add_agent(dynamic_agent)
250
-
251
- # Prepare input data
252
- agent_input_data = (
253
- existing_data
254
- if input_parts
255
- else {"context_info": {"object_type": class_name}}
256
- )
257
-
258
- logger.info(f"Running hydration agent '{agent_name}' for {class_name}...")
259
-
260
- # Run agent
261
- result = await temp_flock.run_async(
262
- agent=agent_name,
263
- input=agent_input_data,
264
- box_result=False,
265
- )
266
- logger.info(f"Hydration agent returned for {class_name}: {list(result.keys())}")
267
-
268
- return result
269
-
270
- except Exception as e:
271
- logger.error(
272
- f"Hydration agent creation or run failed for {class_name}: {e}",
273
- exc_info=True,
274
- )
275
- return None
276
-
277
-
278
- def _update_fields_with_results(
279
- obj: BaseModel,
280
- result: dict[str, Any],
281
- missing_fields: list[str],
282
- class_name: str,
283
- ) -> None:
284
- """Updates object fields with results from the hydration agent."""
285
- updated_count = 0
286
- for field_name in missing_fields:
287
- if field_name in result:
288
- try:
289
- setattr(obj, field_name, result[field_name])
290
- logger.debug(
291
- f"Hydrated field '{field_name}' in {class_name} with value: {getattr(obj, field_name)}"
292
- )
293
- updated_count += 1
294
- except Exception as e:
295
- logger.warning(
296
- f"Failed to set hydrated value for '{field_name}' in {class_name}: {e}. Value received: {result[field_name]}"
297
- )
298
- else:
299
- logger.warning(
300
- f"Hydration result missing expected field for {class_name}: '{field_name}'"
301
- )
302
-
303
- logger.info(
304
- f"Hydration complete for {class_name}. Updated {updated_count}/{len(missing_fields)} fields."
305
- )
306
-
307
-
308
- # Ensure helper functions are available
309
- # from flock.core.serialization.serialization_utils import _format_type_to_string
@@ -1,164 +0,0 @@
1
- """Utility functions for resolving input keys to their corresponding values."""
2
-
3
- from flock.core.context.context import FlockContext
4
- from flock.core.util.splitter import split_top_level
5
-
6
-
7
- def get_callable_members(obj):
8
- """Extract all callable (methods/functions) members from a module or class.
9
- Returns a list of callable objects.
10
- """
11
- import inspect
12
-
13
- # Get all members of the object
14
- members = inspect.getmembers(obj)
15
-
16
- # Filter for callable members that don't start with underscore (to exclude private/special methods)
17
- callables = [
18
- member[1]
19
- for member in members
20
- if inspect.isroutine(member[1]) and not member[0].startswith("_")
21
- ]
22
-
23
- return callables
24
-
25
-
26
- def _parse_keys(keys: list[str]) -> list[str]:
27
- """Split a comma‐separated string and strip any type annotations.
28
-
29
- For example, "a, b: list[str]" becomes ["a", "b"].
30
- """
31
- res_keys = []
32
- for key in keys:
33
- if "|" in key:
34
- key = key.split("|")[0].strip()
35
- if ":" in key:
36
- key = key.split(":")[0].strip()
37
- res_keys.append(key)
38
- return res_keys
39
-
40
-
41
- def top_level_to_keys(s: str) -> list[str]:
42
- """Convert a top-level comma-separated string to a list of keys."""
43
- top_level_split = split_top_level(s)
44
- return _parse_keys(top_level_split)
45
-
46
-
47
- def resolve_inputs(
48
- input_spec: str,
49
- context: FlockContext,
50
- previous_agent_name: str,
51
- previous_agent_output:str,
52
- previous_agent_handoff_strategy:str,
53
- previous_agent_handoff_map:dict[str, str]
54
- ) -> dict:
55
- """Build a dictionary of inputs based on the input specification string and the provided context.
56
-
57
- The lookup rules are:
58
- - "context" (case-insensitive): returns the entire context.
59
- - "context.property": returns an attribute from the context.
60
- - "def.agent_name": returns the agent definition for the given agent.
61
- - "agent_name": returns the most up2date record from the given agent's history.
62
- - "agent_name.property": returns the value of a property from the state variable keyed by "agent_name.property".
63
- - "property": searches the history for the most recent value of a property.
64
- - Otherwise, if no matching value is found, fallback to the FLOCK_INITIAL_INPUT.
65
-
66
- -> Recommendations:
67
- - prefix your agent variables with the agent name or a short handle to avoid conflicts.
68
- eg. agent name: "idea_agent", variable: "ia_idea" (ia = idea agent)
69
- - or set hand off mode to strict to avoid conflicts.
70
- with strict mode, the agent will only accept inputs from the previous agent.
71
-
72
-
73
- Strategy for passing data to the next agent.
74
-
75
- Example:
76
- ReviewAgent.next_agent = SummaryAgent
77
- ReviewAgent(output = "text:str, keywords:list[str], rating:int")
78
- SummaryAgent(input = "text:str, title:str")
79
-
80
- 'append' means the difference in signature is appended to the next agent's input signature.
81
- SummaryAgent(input = "text:str, title:str, keywords:list[str], rating:int")
82
-
83
- 'override' means the target agent's signature is getting overriden.
84
- SummaryAgent(input = "text:str, keywords:list[str], rating:int")
85
-
86
- 'static' means the the target agent's signature is not changed at all.
87
- If source agent has no output fields that match the target agent's input,
88
- there will be no data passed to the next agent.
89
- SummaryAgent(input = "text:str, title:str")
90
-
91
- 'map' means the source agent's output is mapped to the target agent's input
92
- based on 'handoff_map' configuration.
93
-
94
- Args:
95
- input_spec: Comma-separated input keys (e.g., "query" or "agent_name.property").
96
- context: A FlockContext instance.
97
-
98
- Returns:
99
- A dictionary mapping each input key to its resolved value.
100
- """
101
- split_input = split_top_level(input_spec)
102
- keys = _parse_keys(split_input)
103
- inputs = {}
104
-
105
- def _normalize_empty_string(val):
106
- """Treat empty string inputs as None to match None semantics.
107
-
108
- This aligns behavior so passing "" behaves like passing None
109
- for agent input properties.
110
- """
111
- if isinstance(val, str) and val == "":
112
- return None
113
- return val
114
-
115
- for key in keys:
116
- split_key = key.split(".")
117
-
118
- # Case 1: A single key
119
- if len(split_key) == 1:
120
- # Special keyword: "context"
121
- if key.lower() == "context":
122
- inputs[key] = context
123
- continue
124
-
125
- # Try to get a historic record for an agent (if any)
126
- historic_records = context.get_agent_history(key)
127
- if historic_records:
128
- # You may choose to pass the entire record or just its data.
129
- inputs[key] = historic_records[0].data
130
- continue
131
-
132
- # Fallback to the most recent value in the state
133
- historic_value = context.get_most_recent_value(key)
134
- if historic_value is not None:
135
- inputs[key] = _normalize_empty_string(historic_value)
136
- continue
137
-
138
- # Fallback to the initial input
139
- var_value = context.get_variable(key)
140
- if var_value is not None:
141
- inputs[key] = _normalize_empty_string(var_value)
142
- continue
143
-
144
- inputs[key] = _normalize_empty_string(context.get_variable("flock." + key))
145
-
146
- # Case 2: A compound key (e.g., "agent_name.property" or "context.property")
147
- elif len(split_key) == 2:
148
- entity_name, property_name = split_key
149
-
150
- if entity_name.lower() == "context":
151
- # Try to fetch the attribute from the context
152
- inputs[key] = _normalize_empty_string(getattr(context, property_name, None))
153
- continue
154
-
155
- if entity_name.lower() == "def":
156
- # Return the agent definition for the given property name
157
- inputs[key] = _normalize_empty_string(context.get_agent_definition(property_name))
158
- continue
159
-
160
- # Otherwise, attempt to look up a state variable with the key "entity_name.property_name"
161
- inputs[key] = _normalize_empty_string(context.get_variable(f"{entity_name}.{property_name}"))
162
- continue
163
-
164
- return inputs
flock/core/util/loader.py DELETED
@@ -1,59 +0,0 @@
1
- # src/flock/core/loader.py
2
- """Provides functionality to load Flock instances from files."""
3
-
4
- from pathlib import Path
5
- from typing import TYPE_CHECKING
6
-
7
- # Use TYPE_CHECKING to avoid runtime circular import if Flock imports this module indirectly
8
- if TYPE_CHECKING:
9
- from flock.core.flock import Flock
10
-
11
- # Import locally within the function to ensure Serializable methods are available
12
- # from .serialization.serializable import Serializable # Serializable defines the file methods
13
-
14
- # Cloudpickle check needs to be top-level
15
- try:
16
- import cloudpickle
17
-
18
- PICKLE_AVAILABLE = True
19
- except ImportError:
20
- PICKLE_AVAILABLE = False
21
-
22
-
23
- def load_flock_from_file(file_path: str) -> "Flock":
24
- """Load a Flock instance from various file formats (detects type)."""
25
- # Import Flock locally within the function to avoid circular dependency at module level
26
- from flock.core.flock import Flock
27
-
28
- p = Path(file_path)
29
- if not p.exists():
30
- raise FileNotFoundError(f"Flock file not found: {file_path}")
31
-
32
- try:
33
- if p.suffix.lower() in [".yaml", ".yml"]:
34
- return Flock.from_yaml_file(p)
35
- elif p.suffix.lower() == ".json":
36
- # Assuming from_json is available via Serializable or directly on Flock
37
- return Flock.from_json(p.read_text())
38
- elif p.suffix.lower() == ".msgpack":
39
- # Assuming from_msgpack_file is available via Serializable or directly on Flock
40
- return Flock.from_msgpack_file(p)
41
- elif p.suffix.lower() == ".pkl":
42
- if PICKLE_AVAILABLE:
43
- # Assuming from_pickle_file is available via Serializable or directly on Flock
44
- return Flock.from_pickle_file(p)
45
- else:
46
- raise RuntimeError(
47
- "Cannot load Pickle file: cloudpickle not installed."
48
- )
49
- else:
50
- raise ValueError(f"Unsupported file extension: {p.suffix}")
51
- except Exception as e:
52
- # Add specific error logging if helpful
53
- from flock.core.logging.logging import get_logger
54
-
55
- logger = get_logger("loader")
56
- logger.error(
57
- f"Error loading Flock from {file_path}: {e}", exc_info=True
58
- )
59
- raise # Re-raise the original exception