flock-core 0.4.542__py3-none-any.whl → 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of flock-core might be problematic. Click here for more details.

Files changed (501) hide show
  1. flock/__init__.py +12 -217
  2. flock/agent.py +1079 -0
  3. flock/api/themes.py +71 -0
  4. flock/artifacts.py +86 -0
  5. flock/cli.py +147 -0
  6. flock/components.py +189 -0
  7. flock/dashboard/__init__.py +30 -0
  8. flock/dashboard/collector.py +559 -0
  9. flock/dashboard/events.py +188 -0
  10. flock/dashboard/graph_builder.py +563 -0
  11. flock/dashboard/launcher.py +235 -0
  12. flock/dashboard/models/graph.py +156 -0
  13. flock/dashboard/service.py +991 -0
  14. flock/dashboard/static_v2/assets/index-DFRnI_mt.js +111 -0
  15. flock/dashboard/static_v2/assets/index-fPLNdmp1.css +1 -0
  16. flock/dashboard/static_v2/index.html +13 -0
  17. flock/dashboard/websocket.py +246 -0
  18. flock/engines/__init__.py +6 -0
  19. flock/engines/dspy_engine.py +932 -0
  20. flock/examples.py +131 -0
  21. flock/frontend/README.md +778 -0
  22. flock/frontend/docs/DESIGN_SYSTEM.md +1980 -0
  23. flock/frontend/index.html +12 -0
  24. flock/frontend/package-lock.json +4337 -0
  25. flock/frontend/package.json +48 -0
  26. flock/frontend/src/App.tsx +139 -0
  27. flock/frontend/src/__tests__/integration/graph-snapshot.test.tsx +647 -0
  28. flock/frontend/src/__tests__/integration/indexeddb-persistence.test.tsx +699 -0
  29. flock/frontend/src/components/common/BuildInfo.tsx +39 -0
  30. flock/frontend/src/components/common/EmptyState.module.css +115 -0
  31. flock/frontend/src/components/common/EmptyState.tsx +128 -0
  32. flock/frontend/src/components/common/ErrorBoundary.module.css +169 -0
  33. flock/frontend/src/components/common/ErrorBoundary.tsx +118 -0
  34. flock/frontend/src/components/common/KeyboardShortcutsDialog.css +251 -0
  35. flock/frontend/src/components/common/KeyboardShortcutsDialog.tsx +151 -0
  36. flock/frontend/src/components/common/LoadingSpinner.module.css +97 -0
  37. flock/frontend/src/components/common/LoadingSpinner.tsx +29 -0
  38. flock/frontend/src/components/controls/PublishControl.css +547 -0
  39. flock/frontend/src/components/controls/PublishControl.test.tsx +543 -0
  40. flock/frontend/src/components/controls/PublishControl.tsx +432 -0
  41. flock/frontend/src/components/details/DetailWindowContainer.tsx +58 -0
  42. flock/frontend/src/components/details/LiveOutputTab.test.tsx +792 -0
  43. flock/frontend/src/components/details/LiveOutputTab.tsx +220 -0
  44. flock/frontend/src/components/details/MessageDetailWindow.tsx +439 -0
  45. flock/frontend/src/components/details/MessageHistoryTab.tsx +374 -0
  46. flock/frontend/src/components/details/NodeDetailWindow.test.tsx +501 -0
  47. flock/frontend/src/components/details/NodeDetailWindow.tsx +218 -0
  48. flock/frontend/src/components/details/RunStatusTab.tsx +348 -0
  49. flock/frontend/src/components/details/tabs.test.tsx +1015 -0
  50. flock/frontend/src/components/filters/ArtifactTypeFilter.tsx +21 -0
  51. flock/frontend/src/components/filters/CorrelationIDFilter.module.css +102 -0
  52. flock/frontend/src/components/filters/CorrelationIDFilter.test.tsx +197 -0
  53. flock/frontend/src/components/filters/CorrelationIDFilter.tsx +121 -0
  54. flock/frontend/src/components/filters/FilterFlyout.module.css +104 -0
  55. flock/frontend/src/components/filters/FilterFlyout.tsx +80 -0
  56. flock/frontend/src/components/filters/FilterPills.module.css +220 -0
  57. flock/frontend/src/components/filters/FilterPills.test.tsx +189 -0
  58. flock/frontend/src/components/filters/FilterPills.tsx +143 -0
  59. flock/frontend/src/components/filters/ProducerFilter.tsx +21 -0
  60. flock/frontend/src/components/filters/SavedFiltersControl.module.css +60 -0
  61. flock/frontend/src/components/filters/SavedFiltersControl.test.tsx +158 -0
  62. flock/frontend/src/components/filters/SavedFiltersControl.tsx +159 -0
  63. flock/frontend/src/components/filters/TagFilter.tsx +21 -0
  64. flock/frontend/src/components/filters/TimeRangeFilter.module.css +115 -0
  65. flock/frontend/src/components/filters/TimeRangeFilter.test.tsx +154 -0
  66. flock/frontend/src/components/filters/TimeRangeFilter.tsx +110 -0
  67. flock/frontend/src/components/filters/VisibilityFilter.tsx +21 -0
  68. flock/frontend/src/components/graph/AgentNode.test.tsx +77 -0
  69. flock/frontend/src/components/graph/AgentNode.tsx +324 -0
  70. flock/frontend/src/components/graph/GraphCanvas.tsx +613 -0
  71. flock/frontend/src/components/graph/MessageFlowEdge.tsx +128 -0
  72. flock/frontend/src/components/graph/MessageNode.test.tsx +64 -0
  73. flock/frontend/src/components/graph/MessageNode.tsx +129 -0
  74. flock/frontend/src/components/graph/MiniMap.tsx +47 -0
  75. flock/frontend/src/components/graph/TransformEdge.tsx +123 -0
  76. flock/frontend/src/components/layout/DashboardLayout.css +420 -0
  77. flock/frontend/src/components/layout/DashboardLayout.tsx +287 -0
  78. flock/frontend/src/components/layout/Header.module.css +88 -0
  79. flock/frontend/src/components/layout/Header.tsx +52 -0
  80. flock/frontend/src/components/modules/HistoricalArtifactsModule.module.css +288 -0
  81. flock/frontend/src/components/modules/HistoricalArtifactsModule.tsx +450 -0
  82. flock/frontend/src/components/modules/HistoricalArtifactsModuleWrapper.tsx +13 -0
  83. flock/frontend/src/components/modules/JsonAttributeRenderer.tsx +140 -0
  84. flock/frontend/src/components/modules/ModuleRegistry.test.ts +333 -0
  85. flock/frontend/src/components/modules/ModuleRegistry.ts +93 -0
  86. flock/frontend/src/components/modules/ModuleWindow.tsx +223 -0
  87. flock/frontend/src/components/modules/TraceModuleJaeger.tsx +1971 -0
  88. flock/frontend/src/components/modules/TraceModuleJaegerWrapper.tsx +13 -0
  89. flock/frontend/src/components/modules/registerModules.ts +29 -0
  90. flock/frontend/src/components/settings/AdvancedSettings.tsx +175 -0
  91. flock/frontend/src/components/settings/AppearanceSettings.tsx +185 -0
  92. flock/frontend/src/components/settings/GraphSettings.tsx +110 -0
  93. flock/frontend/src/components/settings/MultiSelect.tsx +235 -0
  94. flock/frontend/src/components/settings/SettingsPanel.css +327 -0
  95. flock/frontend/src/components/settings/SettingsPanel.tsx +131 -0
  96. flock/frontend/src/components/settings/ThemeSelector.tsx +298 -0
  97. flock/frontend/src/components/settings/TracingSettings.tsx +404 -0
  98. flock/frontend/src/hooks/useKeyboardShortcuts.ts +148 -0
  99. flock/frontend/src/hooks/useModulePersistence.test.ts +442 -0
  100. flock/frontend/src/hooks/useModulePersistence.ts +154 -0
  101. flock/frontend/src/hooks/useModules.ts +157 -0
  102. flock/frontend/src/hooks/usePersistence.ts +141 -0
  103. flock/frontend/src/main.tsx +13 -0
  104. flock/frontend/src/services/api.ts +337 -0
  105. flock/frontend/src/services/graphService.test.ts +330 -0
  106. flock/frontend/src/services/graphService.ts +75 -0
  107. flock/frontend/src/services/indexeddb.test.ts +793 -0
  108. flock/frontend/src/services/indexeddb.ts +848 -0
  109. flock/frontend/src/services/layout.test.ts +437 -0
  110. flock/frontend/src/services/layout.ts +357 -0
  111. flock/frontend/src/services/themeApplicator.ts +140 -0
  112. flock/frontend/src/services/themeService.ts +77 -0
  113. flock/frontend/src/services/websocket.ts +650 -0
  114. flock/frontend/src/store/filterStore.test.ts +250 -0
  115. flock/frontend/src/store/filterStore.ts +272 -0
  116. flock/frontend/src/store/graphStore.test.ts +570 -0
  117. flock/frontend/src/store/graphStore.ts +462 -0
  118. flock/frontend/src/store/moduleStore.test.ts +253 -0
  119. flock/frontend/src/store/moduleStore.ts +75 -0
  120. flock/frontend/src/store/settingsStore.ts +188 -0
  121. flock/frontend/src/store/streamStore.ts +68 -0
  122. flock/frontend/src/store/uiStore.test.ts +54 -0
  123. flock/frontend/src/store/uiStore.ts +122 -0
  124. flock/frontend/src/store/wsStore.ts +34 -0
  125. flock/frontend/src/styles/index.css +15 -0
  126. flock/frontend/src/styles/scrollbar.css +47 -0
  127. flock/frontend/src/styles/variables.css +488 -0
  128. flock/frontend/src/test/setup.ts +1 -0
  129. flock/frontend/src/types/filters.ts +47 -0
  130. flock/frontend/src/types/graph.ts +95 -0
  131. flock/frontend/src/types/modules.ts +10 -0
  132. flock/frontend/src/types/theme.ts +55 -0
  133. flock/frontend/src/utils/artifacts.ts +24 -0
  134. flock/frontend/src/utils/mockData.ts +98 -0
  135. flock/frontend/src/utils/performance.ts +16 -0
  136. flock/frontend/src/vite-env.d.ts +17 -0
  137. flock/frontend/tsconfig.json +27 -0
  138. flock/frontend/tsconfig.node.json +11 -0
  139. flock/frontend/vite.config.ts +25 -0
  140. flock/frontend/vitest.config.ts +11 -0
  141. flock/{core/util → helper}/cli_helper.py +9 -5
  142. flock/{core/logging → logging}/__init__.py +2 -3
  143. flock/logging/auto_trace.py +159 -0
  144. flock/{core/logging → logging}/formatters/enum_builder.py +3 -4
  145. flock/{core/logging → logging}/formatters/theme_builder.py +19 -44
  146. flock/{core/logging → logging}/formatters/themed_formatter.py +69 -107
  147. flock/{core/logging → logging}/logging.py +78 -61
  148. flock/{core/logging → logging}/telemetry.py +66 -26
  149. flock/{core/logging → logging}/telemetry_exporter/base_exporter.py +2 -2
  150. flock/logging/telemetry_exporter/duckdb_exporter.py +216 -0
  151. flock/{core/logging → logging}/telemetry_exporter/file_exporter.py +13 -10
  152. flock/{core/logging → logging}/telemetry_exporter/sqlite_exporter.py +2 -3
  153. flock/logging/trace_and_logged.py +304 -0
  154. flock/mcp/__init__.py +91 -0
  155. flock/{core/mcp/mcp_client.py → mcp/client.py} +131 -158
  156. flock/{core/mcp/mcp_config.py → mcp/config.py} +86 -132
  157. flock/mcp/manager.py +286 -0
  158. flock/mcp/servers/sse/__init__.py +1 -1
  159. flock/mcp/servers/sse/flock_sse_server.py +16 -58
  160. flock/mcp/servers/stdio/__init__.py +1 -1
  161. flock/mcp/servers/stdio/flock_stdio_server.py +13 -53
  162. flock/mcp/servers/streamable_http/flock_streamable_http_server.py +22 -67
  163. flock/mcp/servers/websockets/flock_websocket_server.py +12 -45
  164. flock/{core/mcp/flock_mcp_tool_base.py → mcp/tool.py} +24 -78
  165. flock/mcp/types/__init__.py +42 -0
  166. flock/{core/mcp → mcp}/types/callbacks.py +12 -15
  167. flock/{core/mcp → mcp}/types/factories.py +7 -6
  168. flock/{core/mcp → mcp}/types/handlers.py +13 -18
  169. flock/{core/mcp → mcp}/types/types.py +70 -74
  170. flock/{core/mcp → mcp}/util/helpers.py +3 -3
  171. flock/orchestrator.py +970 -0
  172. flock/registry.py +148 -0
  173. flock/runtime.py +262 -0
  174. flock/service.py +277 -0
  175. flock/store.py +1214 -0
  176. flock/subscription.py +111 -0
  177. flock/themes/andromeda.toml +1 -1
  178. flock/themes/apple-system-colors.toml +1 -1
  179. flock/themes/arcoiris.toml +1 -1
  180. flock/themes/atomonelight.toml +1 -1
  181. flock/themes/ayu copy.toml +1 -1
  182. flock/themes/ayu-light.toml +1 -1
  183. flock/themes/belafonte-day.toml +1 -1
  184. flock/themes/belafonte-night.toml +1 -1
  185. flock/themes/blulocodark.toml +1 -1
  186. flock/themes/breeze.toml +1 -1
  187. flock/themes/broadcast.toml +1 -1
  188. flock/themes/brogrammer.toml +1 -1
  189. flock/themes/builtin-dark.toml +1 -1
  190. flock/themes/builtin-pastel-dark.toml +1 -1
  191. flock/themes/catppuccin-latte.toml +1 -1
  192. flock/themes/catppuccin-macchiato.toml +1 -1
  193. flock/themes/catppuccin-mocha.toml +1 -1
  194. flock/themes/cga.toml +1 -1
  195. flock/themes/chalk.toml +1 -1
  196. flock/themes/ciapre.toml +1 -1
  197. flock/themes/coffee-theme.toml +1 -1
  198. flock/themes/cyberpunkscarletprotocol.toml +1 -1
  199. flock/themes/dark+.toml +1 -1
  200. flock/themes/darkermatrix.toml +1 -1
  201. flock/themes/darkmatrix.toml +2 -2
  202. flock/themes/darkside.toml +1 -1
  203. flock/themes/deep.toml +2 -2
  204. flock/themes/desert.toml +1 -1
  205. flock/themes/django.toml +1 -1
  206. flock/themes/djangosmooth.toml +1 -1
  207. flock/themes/doomone.toml +1 -1
  208. flock/themes/dotgov.toml +1 -1
  209. flock/themes/dracula+.toml +1 -1
  210. flock/themes/duckbones.toml +1 -1
  211. flock/themes/encom.toml +1 -1
  212. flock/themes/espresso.toml +1 -1
  213. flock/themes/everblush.toml +1 -1
  214. flock/themes/fairyfloss.toml +1 -1
  215. flock/themes/fideloper.toml +1 -1
  216. flock/themes/fishtank.toml +1 -1
  217. flock/themes/flexoki-light.toml +1 -1
  218. flock/themes/floraverse.toml +1 -1
  219. flock/themes/framer.toml +1 -1
  220. flock/themes/galizur.toml +1 -1
  221. flock/themes/github.toml +1 -1
  222. flock/themes/grass.toml +1 -1
  223. flock/themes/grey-green.toml +1 -1
  224. flock/themes/gruvboxlight.toml +1 -1
  225. flock/themes/guezwhoz.toml +1 -1
  226. flock/themes/harper.toml +1 -1
  227. flock/themes/hax0r-blue.toml +1 -1
  228. flock/themes/hopscotch.256.toml +1 -1
  229. flock/themes/ic-green-ppl.toml +1 -1
  230. flock/themes/iceberg-dark.toml +1 -1
  231. flock/themes/japanesque.toml +1 -1
  232. flock/themes/jubi.toml +1 -1
  233. flock/themes/kibble.toml +1 -1
  234. flock/themes/kolorit.toml +1 -1
  235. flock/themes/kurokula.toml +1 -1
  236. flock/themes/materialdesigncolors.toml +1 -1
  237. flock/themes/matrix.toml +1 -1
  238. flock/themes/mellifluous.toml +1 -1
  239. flock/themes/midnight-in-mojave.toml +1 -1
  240. flock/themes/monokai-remastered.toml +1 -1
  241. flock/themes/monokai-soda.toml +1 -1
  242. flock/themes/neon.toml +1 -1
  243. flock/themes/neopolitan.toml +5 -5
  244. flock/themes/nord-light.toml +1 -1
  245. flock/themes/ocean.toml +1 -1
  246. flock/themes/onehalfdark.toml +1 -1
  247. flock/themes/onehalflight.toml +1 -1
  248. flock/themes/palenighthc.toml +1 -1
  249. flock/themes/paulmillr.toml +1 -1
  250. flock/themes/pencildark.toml +1 -1
  251. flock/themes/pnevma.toml +1 -1
  252. flock/themes/purple-rain.toml +1 -1
  253. flock/themes/purplepeter.toml +1 -1
  254. flock/themes/raycast-dark.toml +1 -1
  255. flock/themes/red-sands.toml +1 -1
  256. flock/themes/relaxed.toml +1 -1
  257. flock/themes/retro.toml +1 -1
  258. flock/themes/rose-pine.toml +1 -1
  259. flock/themes/royal.toml +1 -1
  260. flock/themes/ryuuko.toml +1 -1
  261. flock/themes/sakura.toml +1 -1
  262. flock/themes/scarlet-protocol.toml +1 -1
  263. flock/themes/seoulbones-dark.toml +1 -1
  264. flock/themes/shades-of-purple.toml +1 -1
  265. flock/themes/smyck.toml +1 -1
  266. flock/themes/softserver.toml +1 -1
  267. flock/themes/solarized-darcula.toml +1 -1
  268. flock/themes/square.toml +1 -1
  269. flock/themes/sugarplum.toml +1 -1
  270. flock/themes/thayer-bright.toml +1 -1
  271. flock/themes/tokyonight.toml +1 -1
  272. flock/themes/tomorrow.toml +1 -1
  273. flock/themes/ubuntu.toml +1 -1
  274. flock/themes/ultradark.toml +1 -1
  275. flock/themes/ultraviolent.toml +1 -1
  276. flock/themes/unikitty.toml +1 -1
  277. flock/themes/urple.toml +1 -1
  278. flock/themes/vesper.toml +1 -1
  279. flock/themes/vimbones.toml +1 -1
  280. flock/themes/wildcherry.toml +1 -1
  281. flock/themes/wilmersdorf.toml +1 -1
  282. flock/themes/wryan.toml +1 -1
  283. flock/themes/xcodedarkhc.toml +1 -1
  284. flock/themes/xcodelight.toml +1 -1
  285. flock/themes/zenbones-light.toml +1 -1
  286. flock/themes/zenwritten-dark.toml +1 -1
  287. flock/utilities.py +301 -0
  288. flock/utility/output_utility_component.py +226 -0
  289. flock/visibility.py +107 -0
  290. flock_core-0.5.0.dist-info/METADATA +964 -0
  291. flock_core-0.5.0.dist-info/RECORD +525 -0
  292. flock_core-0.5.0.dist-info/entry_points.txt +2 -0
  293. {flock_core-0.4.542.dist-info → flock_core-0.5.0.dist-info}/licenses/LICENSE +1 -1
  294. flock/adapter/__init__.py +0 -14
  295. flock/adapter/azure_adapter.py +0 -68
  296. flock/adapter/chroma_adapter.py +0 -73
  297. flock/adapter/faiss_adapter.py +0 -97
  298. flock/adapter/pinecone_adapter.py +0 -51
  299. flock/adapter/vector_base.py +0 -47
  300. flock/cli/assets/release_notes.md +0 -140
  301. flock/cli/config.py +0 -8
  302. flock/cli/constants.py +0 -36
  303. flock/cli/create_agent.py +0 -1
  304. flock/cli/create_flock.py +0 -280
  305. flock/cli/execute_flock.py +0 -620
  306. flock/cli/load_agent.py +0 -1
  307. flock/cli/load_examples.py +0 -1
  308. flock/cli/load_flock.py +0 -192
  309. flock/cli/load_release_notes.py +0 -20
  310. flock/cli/loaded_flock_cli.py +0 -254
  311. flock/cli/manage_agents.py +0 -459
  312. flock/cli/registry_management.py +0 -889
  313. flock/cli/runner.py +0 -41
  314. flock/cli/settings.py +0 -857
  315. flock/cli/utils.py +0 -135
  316. flock/cli/view_results.py +0 -29
  317. flock/cli/yaml_editor.py +0 -396
  318. flock/config.py +0 -56
  319. flock/core/__init__.py +0 -44
  320. flock/core/api/__init__.py +0 -10
  321. flock/core/api/custom_endpoint.py +0 -45
  322. flock/core/api/endpoints.py +0 -262
  323. flock/core/api/main.py +0 -162
  324. flock/core/api/models.py +0 -101
  325. flock/core/api/run_store.py +0 -224
  326. flock/core/api/runner.py +0 -44
  327. flock/core/api/service.py +0 -214
  328. flock/core/config/flock_agent_config.py +0 -11
  329. flock/core/config/scheduled_agent_config.py +0 -40
  330. flock/core/context/context.py +0 -214
  331. flock/core/context/context_manager.py +0 -40
  332. flock/core/context/context_vars.py +0 -11
  333. flock/core/evaluation/utils.py +0 -395
  334. flock/core/execution/batch_executor.py +0 -369
  335. flock/core/execution/evaluation_executor.py +0 -438
  336. flock/core/execution/local_executor.py +0 -31
  337. flock/core/execution/opik_executor.py +0 -103
  338. flock/core/execution/temporal_executor.py +0 -166
  339. flock/core/flock.py +0 -1003
  340. flock/core/flock_agent.py +0 -1258
  341. flock/core/flock_evaluator.py +0 -60
  342. flock/core/flock_factory.py +0 -513
  343. flock/core/flock_module.py +0 -207
  344. flock/core/flock_registry.py +0 -702
  345. flock/core/flock_router.py +0 -83
  346. flock/core/flock_scheduler.py +0 -166
  347. flock/core/flock_server_manager.py +0 -136
  348. flock/core/interpreter/python_interpreter.py +0 -689
  349. flock/core/logging/live_capture.py +0 -137
  350. flock/core/logging/trace_and_logged.py +0 -59
  351. flock/core/mcp/__init__.py +0 -1
  352. flock/core/mcp/flock_mcp_server.py +0 -640
  353. flock/core/mcp/mcp_client_manager.py +0 -201
  354. flock/core/mcp/types/__init__.py +0 -1
  355. flock/core/mixin/dspy_integration.py +0 -445
  356. flock/core/mixin/prompt_parser.py +0 -125
  357. flock/core/serialization/__init__.py +0 -13
  358. flock/core/serialization/callable_registry.py +0 -52
  359. flock/core/serialization/flock_serializer.py +0 -854
  360. flock/core/serialization/json_encoder.py +0 -41
  361. flock/core/serialization/secure_serializer.py +0 -175
  362. flock/core/serialization/serializable.py +0 -342
  363. flock/core/serialization/serialization_utils.py +0 -409
  364. flock/core/util/file_path_utils.py +0 -223
  365. flock/core/util/hydrator.py +0 -309
  366. flock/core/util/input_resolver.py +0 -141
  367. flock/core/util/loader.py +0 -59
  368. flock/core/util/splitter.py +0 -219
  369. flock/di.py +0 -41
  370. flock/evaluators/__init__.py +0 -1
  371. flock/evaluators/declarative/__init__.py +0 -1
  372. flock/evaluators/declarative/declarative_evaluator.py +0 -217
  373. flock/evaluators/memory/memory_evaluator.py +0 -90
  374. flock/evaluators/test/test_case_evaluator.py +0 -38
  375. flock/evaluators/zep/zep_evaluator.py +0 -59
  376. flock/modules/__init__.py +0 -1
  377. flock/modules/assertion/__init__.py +0 -1
  378. flock/modules/assertion/assertion_module.py +0 -286
  379. flock/modules/callback/__init__.py +0 -1
  380. flock/modules/callback/callback_module.py +0 -91
  381. flock/modules/enterprise_memory/README.md +0 -99
  382. flock/modules/enterprise_memory/enterprise_memory_module.py +0 -526
  383. flock/modules/mem0/__init__.py +0 -1
  384. flock/modules/mem0/mem0_module.py +0 -126
  385. flock/modules/mem0_async/__init__.py +0 -1
  386. flock/modules/mem0_async/async_mem0_module.py +0 -126
  387. flock/modules/memory/__init__.py +0 -1
  388. flock/modules/memory/memory_module.py +0 -429
  389. flock/modules/memory/memory_parser.py +0 -125
  390. flock/modules/memory/memory_storage.py +0 -736
  391. flock/modules/output/__init__.py +0 -1
  392. flock/modules/output/output_module.py +0 -196
  393. flock/modules/performance/__init__.py +0 -1
  394. flock/modules/performance/metrics_module.py +0 -678
  395. flock/modules/zep/__init__.py +0 -1
  396. flock/modules/zep/zep_module.py +0 -192
  397. flock/platform/docker_tools.py +0 -49
  398. flock/platform/jaeger_install.py +0 -86
  399. flock/routers/__init__.py +0 -1
  400. flock/routers/agent/__init__.py +0 -1
  401. flock/routers/agent/agent_router.py +0 -236
  402. flock/routers/agent/handoff_agent.py +0 -58
  403. flock/routers/conditional/conditional_router.py +0 -486
  404. flock/routers/default/__init__.py +0 -1
  405. flock/routers/default/default_router.py +0 -80
  406. flock/routers/feedback/feedback_router.py +0 -114
  407. flock/routers/list_generator/list_generator_router.py +0 -166
  408. flock/routers/llm/__init__.py +0 -1
  409. flock/routers/llm/llm_router.py +0 -365
  410. flock/tools/__init__.py +0 -0
  411. flock/tools/azure_tools.py +0 -781
  412. flock/tools/code_tools.py +0 -167
  413. flock/tools/file_tools.py +0 -149
  414. flock/tools/github_tools.py +0 -157
  415. flock/tools/markdown_tools.py +0 -205
  416. flock/tools/system_tools.py +0 -9
  417. flock/tools/text_tools.py +0 -810
  418. flock/tools/web_tools.py +0 -92
  419. flock/tools/zendesk_tools.py +0 -501
  420. flock/webapp/__init__.py +0 -1
  421. flock/webapp/app/__init__.py +0 -0
  422. flock/webapp/app/api/__init__.py +0 -0
  423. flock/webapp/app/api/agent_management.py +0 -237
  424. flock/webapp/app/api/execution.py +0 -503
  425. flock/webapp/app/api/flock_management.py +0 -125
  426. flock/webapp/app/api/registry_viewer.py +0 -29
  427. flock/webapp/app/chat.py +0 -662
  428. flock/webapp/app/config.py +0 -104
  429. flock/webapp/app/dependencies.py +0 -117
  430. flock/webapp/app/main.py +0 -1086
  431. flock/webapp/app/middleware.py +0 -113
  432. flock/webapp/app/models_ui.py +0 -7
  433. flock/webapp/app/services/__init__.py +0 -0
  434. flock/webapp/app/services/feedback_file_service.py +0 -363
  435. flock/webapp/app/services/flock_service.py +0 -345
  436. flock/webapp/app/services/sharing_models.py +0 -81
  437. flock/webapp/app/services/sharing_store.py +0 -597
  438. flock/webapp/app/templates/theme_mapper.html +0 -326
  439. flock/webapp/app/theme_mapper.py +0 -811
  440. flock/webapp/app/utils.py +0 -85
  441. flock/webapp/run.py +0 -219
  442. flock/webapp/static/css/chat.css +0 -301
  443. flock/webapp/static/css/components.css +0 -167
  444. flock/webapp/static/css/header.css +0 -39
  445. flock/webapp/static/css/layout.css +0 -281
  446. flock/webapp/static/css/sidebar.css +0 -127
  447. flock/webapp/static/css/two-pane.css +0 -48
  448. flock/webapp/templates/base.html +0 -389
  449. flock/webapp/templates/chat.html +0 -152
  450. flock/webapp/templates/chat_settings.html +0 -19
  451. flock/webapp/templates/flock_editor.html +0 -16
  452. flock/webapp/templates/index.html +0 -12
  453. flock/webapp/templates/partials/_agent_detail_form.html +0 -93
  454. flock/webapp/templates/partials/_agent_list.html +0 -18
  455. flock/webapp/templates/partials/_agent_manager_view.html +0 -51
  456. flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
  457. flock/webapp/templates/partials/_chat_container.html +0 -15
  458. flock/webapp/templates/partials/_chat_messages.html +0 -57
  459. flock/webapp/templates/partials/_chat_settings_form.html +0 -85
  460. flock/webapp/templates/partials/_create_flock_form.html +0 -50
  461. flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
  462. flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
  463. flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
  464. flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
  465. flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
  466. flock/webapp/templates/partials/_env_vars_table.html +0 -23
  467. flock/webapp/templates/partials/_execution_form.html +0 -127
  468. flock/webapp/templates/partials/_execution_view_container.html +0 -28
  469. flock/webapp/templates/partials/_flock_file_list.html +0 -23
  470. flock/webapp/templates/partials/_flock_properties_form.html +0 -52
  471. flock/webapp/templates/partials/_flock_upload_form.html +0 -16
  472. flock/webapp/templates/partials/_header_flock_status.html +0 -5
  473. flock/webapp/templates/partials/_live_logs.html +0 -13
  474. flock/webapp/templates/partials/_load_manager_view.html +0 -49
  475. flock/webapp/templates/partials/_registry_table.html +0 -25
  476. flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
  477. flock/webapp/templates/partials/_results_display.html +0 -78
  478. flock/webapp/templates/partials/_settings_env_content.html +0 -9
  479. flock/webapp/templates/partials/_settings_theme_content.html +0 -14
  480. flock/webapp/templates/partials/_settings_view.html +0 -36
  481. flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
  482. flock/webapp/templates/partials/_share_link_snippet.html +0 -35
  483. flock/webapp/templates/partials/_sidebar.html +0 -74
  484. flock/webapp/templates/partials/_structured_data_view.html +0 -40
  485. flock/webapp/templates/partials/_theme_preview.html +0 -36
  486. flock/webapp/templates/registry_viewer.html +0 -84
  487. flock/webapp/templates/shared_run_page.html +0 -140
  488. flock/workflow/__init__.py +0 -0
  489. flock/workflow/activities.py +0 -237
  490. flock/workflow/agent_activities.py +0 -24
  491. flock/workflow/agent_execution_activity.py +0 -240
  492. flock/workflow/flock_workflow.py +0 -225
  493. flock/workflow/temporal_config.py +0 -96
  494. flock/workflow/temporal_setup.py +0 -60
  495. flock_core-0.4.542.dist-info/METADATA +0 -676
  496. flock_core-0.4.542.dist-info/RECORD +0 -572
  497. flock_core-0.4.542.dist-info/entry_points.txt +0 -2
  498. /flock/{core/logging → logging}/formatters/themes.py +0 -0
  499. /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
  500. /flock/{core/mcp → mcp}/util/__init__.py +0 -0
  501. {flock_core-0.4.542.dist-info → flock_core-0.5.0.dist-info}/WHEEL +0 -0
flock/core/flock_agent.py DELETED
@@ -1,1258 +0,0 @@
1
- # src/flock/core/flock_agent.py
2
- """FlockAgent is the core, declarative base class for all agents in the Flock framework."""
3
-
4
- import asyncio
5
- import json
6
- import os
7
- import uuid
8
- from abc import ABC
9
- from collections.abc import Callable
10
- from datetime import datetime
11
- from typing import TYPE_CHECKING, Any, TypeVar
12
-
13
- from flock.core.config.flock_agent_config import FlockAgentConfig
14
- from flock.core.mcp.flock_mcp_server import FlockMCPServerBase
15
- from flock.core.serialization.json_encoder import FlockJSONEncoder
16
- from flock.workflow.temporal_config import TemporalActivityConfig
17
-
18
- if TYPE_CHECKING:
19
- from flock.core.context.context import FlockContext
20
- from flock.core.flock_evaluator import FlockEvaluator
21
- from flock.core.flock_module import FlockModule
22
- from flock.core.flock_router import FlockRouter
23
-
24
- from opentelemetry import trace
25
- from pydantic import BaseModel, Field
26
-
27
- # Core Flock components (ensure these are importable)
28
- from flock.core.context.context import FlockContext
29
- from flock.core.flock_evaluator import FlockEvaluator, FlockEvaluatorConfig
30
- from flock.core.flock_module import FlockModule, FlockModuleConfig
31
- from flock.core.flock_router import FlockRouter, FlockRouterConfig
32
- from flock.core.logging.logging import get_logger
33
-
34
- # Mixins and Serialization components
35
- from flock.core.mixin.dspy_integration import DSPyIntegrationMixin
36
- from flock.core.serialization.serializable import (
37
- Serializable, # Import Serializable base
38
- )
39
- from flock.core.serialization.serialization_utils import (
40
- deserialize_component,
41
- serialize_item,
42
- )
43
-
44
- logger = get_logger("agent")
45
- tracer = trace.get_tracer(__name__)
46
- T = TypeVar("T", bound="FlockAgent")
47
-
48
-
49
- SignatureType = (
50
- str
51
- | Callable[..., str]
52
- | type[BaseModel]
53
- | Callable[..., type[BaseModel]]
54
- | None
55
- )
56
-
57
-
58
- # Make FlockAgent inherit from Serializable
59
- class FlockAgent(BaseModel, Serializable, DSPyIntegrationMixin, ABC):
60
- """Core, declarative base class for Flock agents, enabling serialization,
61
- modularity, and integration with evaluation and routing components.
62
- Inherits from Pydantic BaseModel, ABC, DSPyIntegrationMixin, and Serializable.
63
- """
64
-
65
- agent_id: str = Field(
66
- default_factory=lambda: str(uuid.uuid4()),
67
- description="Internal, Unique UUID4 for this agent instance. No need to set it manually. Used for MCP features.",
68
- )
69
-
70
- name: str = Field(..., description="Unique identifier for the agent.")
71
-
72
- model: str | None = Field(
73
- None,
74
- description="The model identifier to use (e.g., 'openai/gpt-4o'). If None, uses Flock's default.",
75
- )
76
- description: str | Callable[..., str] | None = Field(
77
- "",
78
- description="A human-readable description or a callable returning one.",
79
- )
80
- input: SignatureType = Field(
81
- None,
82
- description=(
83
- "Signature for input keys. Supports type hints (:) and descriptions (|). "
84
- "E.g., 'query: str | Search query, context: dict | Conversation context'. Can be a callable."
85
- ),
86
- )
87
- output: SignatureType = Field(
88
- None,
89
- description=(
90
- "Signature for output keys. Supports type hints (:) and descriptions (|). "
91
- "E.g., 'result: str | Generated result, summary: str | Brief summary'. Can be a callable."
92
- ),
93
- )
94
- tools: list[Callable[..., Any]] | None = (
95
- Field( # Assume tools are always callable for serialization simplicity
96
- default=None,
97
- description="List of callable tools the agent can use for development/default runs.",
98
- )
99
- )
100
- production_tools: list[Callable[..., Any]] | None = Field(
101
- default=None,
102
- description="Optional list of callable tools to use when production tool set is enabled.",
103
- )
104
- servers: list[str | FlockMCPServerBase] | None = Field(
105
- default=None,
106
- description="List of MCP Servers the agent can use to enhance its capabilities. These must be registered.",
107
- )
108
-
109
- write_to_file: bool = Field(
110
- default=False,
111
- description="Write the agent's output to a file.",
112
- )
113
- wait_for_input: bool = Field(
114
- default=False,
115
- description="Wait for user input after the agent's output is displayed.",
116
- )
117
-
118
- # --- Components ---
119
- evaluator: FlockEvaluator | None = Field( # Make optional, allow None
120
- default=None,
121
- description="The evaluator instance defining the agent's core logic.",
122
- )
123
- handoff_router: FlockRouter | None = Field( # Make optional, allow None
124
- default=None,
125
- description="Router determining the next agent in the workflow.",
126
- )
127
- modules: dict[str, FlockModule] = Field( # Keep as dict
128
- default_factory=dict,
129
- description="Dictionary of FlockModules attached to this agent.",
130
- )
131
-
132
- config: FlockAgentConfig = Field(
133
- default_factory=lambda: FlockAgentConfig(),
134
- description="Configuration for this agent, holding various settings and parameters.",
135
- )
136
-
137
- # --- Temporal Configuration (Optional) ---
138
- temporal_activity_config: TemporalActivityConfig | None = Field(
139
- default=None,
140
- description="Optional Temporal settings specific to this agent's activity execution.",
141
- )
142
-
143
- # --- Runtime State (Excluded from Serialization) ---
144
- context: FlockContext | None = Field(
145
- default=None,
146
- exclude=True, # Exclude context from model_dump and serialization
147
- description="Runtime context associated with the flock execution.",
148
- )
149
-
150
- def __init__(
151
- self,
152
- name: str,
153
- model: str | None = None,
154
- description: str | Callable[..., str] | None = "",
155
- input: SignatureType = None,
156
- output: SignatureType = None,
157
- tools: list[Callable[..., Any]] | None = None,
158
- production_tools: list[Callable[..., Any]] | None = None,
159
- servers: list[str | FlockMCPServerBase] | None = None,
160
- evaluator: "FlockEvaluator | None" = None,
161
- handoff_router: "FlockRouter | None" = None,
162
- # Use dict for modules
163
- modules: dict[str, "FlockModule"] | None = None,
164
- write_to_file: bool = False,
165
- wait_for_input: bool = False,
166
- temporal_activity_config: TemporalActivityConfig | None = None,
167
- **kwargs,
168
- ):
169
- super().__init__(
170
- name=name,
171
- model=model,
172
- description=description,
173
- input=input, # Store the raw input spec
174
- output=output, # Store the raw output spec
175
- tools=tools,
176
- production_tools=production_tools,
177
- servers=servers,
178
- write_to_file=write_to_file,
179
- wait_for_input=wait_for_input,
180
- evaluator=evaluator,
181
- handoff_router=handoff_router,
182
- modules=modules
183
- if modules is not None
184
- else {}, # Ensure modules is a dict
185
- temporal_activity_config=temporal_activity_config,
186
- **kwargs,
187
- )
188
-
189
- if isinstance(self.input, type) and issubclass(self.input, BaseModel):
190
- self._input_model = self.input
191
- if isinstance(self.output, type) and issubclass(self.output, BaseModel):
192
- self._output_model = self.output
193
-
194
- # --- Existing Methods (add_module, remove_module, etc.) ---
195
- # (Keep these methods as they were, adding type hints where useful)
196
- def add_module(self, module: FlockModule) -> None:
197
- """Add a module to this agent."""
198
- if not module.name:
199
- logger.error("Module must have a name to be added.")
200
- return
201
- if module.name in self.modules:
202
- logger.warning(f"Overwriting existing module: {module.name}")
203
- self.modules[module.name] = module
204
- logger.debug(f"Added module '{module.name}' to agent '{self.name}'")
205
-
206
- def remove_module(self, module_name: str) -> None:
207
- """Remove a module from this agent."""
208
- if module_name in self.modules:
209
- del self.modules[module_name]
210
- logger.debug(
211
- f"Removed module '{module_name}' from agent '{self.name}'"
212
- )
213
- else:
214
- logger.warning(
215
- f"Module '{module_name}' not found on agent '{self.name}'."
216
- )
217
-
218
- def get_module(self, module_name: str) -> FlockModule | None:
219
- """Get a module by name."""
220
- return self.modules.get(module_name)
221
-
222
- def get_enabled_modules(self) -> list[FlockModule]:
223
- """Get a list of currently enabled modules attached to this agent."""
224
- return [m for m in self.modules.values() if m.config.enabled]
225
-
226
- @property
227
- def resolved_description(self) -> str | None:
228
- """Returns the resolved agent description.
229
- If the description is a callable, it attempts to call it.
230
- Returns None if the description is None or a callable that fails.
231
- """
232
- if callable(self.description):
233
- try:
234
- # Attempt to call without context first.
235
- # If callables consistently need context, this might need adjustment
236
- # or the template-facing property might need to be simpler,
237
- # relying on prior resolution via resolve_callables.
238
- return self.description()
239
- except TypeError:
240
- # Log a warning that context might be needed?
241
- # For now, treat as unresolvable in this simple property.
242
- logger.warning(
243
- f"Callable description for agent '{self.name}' could not be resolved "
244
- f"without context via the simple 'resolved_description' property. "
245
- f"Consider calling 'agent.resolve_callables(context)' beforehand if context is required."
246
- )
247
- return None # Or a placeholder like "[Callable Description]"
248
- except Exception as e:
249
- logger.error(
250
- f"Error resolving callable description for agent '{self.name}': {e}"
251
- )
252
- return None
253
- elif isinstance(self.description, str):
254
- return self.description
255
- return None
256
-
257
- # --- Lifecycle Hooks (Keep as they were) ---
258
- async def initialize(self, inputs: dict[str, Any]) -> None:
259
- """Initialize agent and run module initializers."""
260
- logger.debug(f"Initializing agent '{self.name}'")
261
- with tracer.start_as_current_span("agent.initialize") as span:
262
- span.set_attribute("agent.name", self.name)
263
- span.set_attribute("inputs", str(inputs))
264
- logger.info(
265
- f"agent.initialize",
266
- agent=self.name,
267
- )
268
- try:
269
- for module in self.get_enabled_modules():
270
- await module.on_initialize(self, inputs, self.context)
271
- except Exception as module_error:
272
- logger.error(
273
- "Error during initialize",
274
- agent=self.name,
275
- error=str(module_error),
276
- )
277
- span.record_exception(module_error)
278
-
279
- async def terminate(
280
- self, inputs: dict[str, Any], result: dict[str, Any]
281
- ) -> None:
282
- """Terminate agent and run module terminators."""
283
- logger.debug(f"Terminating agent '{self.name}'")
284
- with tracer.start_as_current_span("agent.terminate") as span:
285
- span.set_attribute("agent.name", self.name)
286
- span.set_attribute("inputs", str(inputs))
287
- span.set_attribute("result", str(result))
288
- logger.info(
289
- f"agent.terminate",
290
- agent=self.name,
291
- )
292
- try:
293
- current_result = result
294
- for module in self.get_enabled_modules():
295
- tmp_result = await module.on_terminate(
296
- self, inputs, self.context, current_result
297
- )
298
- # If the module returns a result, use it
299
- if tmp_result:
300
- current_result = tmp_result
301
-
302
- if self.write_to_file:
303
- self._save_output(self.name, current_result)
304
-
305
- if self.wait_for_input:
306
- # simple input prompt
307
- input("Press Enter to continue...")
308
-
309
- except Exception as module_error:
310
- logger.error(
311
- "Error during terminate",
312
- agent=self.name,
313
- error=str(module_error),
314
- )
315
- span.record_exception(module_error)
316
-
317
- async def on_error(self, error: Exception, inputs: dict[str, Any]) -> None:
318
- """Handle errors and run module error handlers."""
319
- logger.error(f"Error occurred in agent '{self.name}': {error}")
320
- with tracer.start_as_current_span("agent.on_error") as span:
321
- span.set_attribute("agent.name", self.name)
322
- span.set_attribute("inputs", str(inputs))
323
- try:
324
- for module in self.get_enabled_modules():
325
- await module.on_error(self, inputs, self.context, error)
326
- except Exception as module_error:
327
- logger.error(
328
- "Error during on_error",
329
- agent=self.name,
330
- error=str(module_error),
331
- )
332
- span.record_exception(module_error)
333
-
334
- def _get_runtime_tools(self, use_production_tools: bool | None = None) -> list[Callable[..., Any]]:
335
- """Select the tool list for the current run."""
336
- if use_production_tools and self.production_tools:
337
- logger.info(
338
- "Using production tool set",
339
- agent=self.name,
340
- tool_count=len(self.production_tools),
341
- )
342
- return list(self.production_tools)
343
-
344
- if use_production_tools and not self.production_tools:
345
- logger.warning(
346
- "Production tool set requested but not configured; falling back to default tools",
347
- agent=self.name,
348
- )
349
-
350
- return list(self.tools or [])
351
-
352
- async def evaluate(
353
- self, inputs: dict[str, Any], *, use_production_tools: bool | None = None
354
- ) -> dict[str, Any]:
355
- """Core evaluation logic, calling the assigned evaluator and modules."""
356
- if not self.evaluator:
357
- raise RuntimeError(
358
- f"Agent '{self.name}' has no evaluator assigned."
359
- )
360
- with tracer.start_as_current_span("agent.evaluate") as span:
361
- span.set_attribute("agent.name", self.name)
362
- span.set_attribute("inputs", str(inputs))
363
- logger.info(
364
- f"agent.evaluate",
365
- agent=self.name,
366
- )
367
-
368
- logger.debug(f"Evaluating agent '{self.name}'")
369
- current_inputs = inputs
370
-
371
- # Pre-evaluate hooks
372
- for module in self.get_enabled_modules():
373
- current_inputs = await module.on_pre_evaluate(
374
- self, current_inputs, self.context
375
- )
376
-
377
- # Actual evaluation
378
- try:
379
- # Pass registered tools if the evaluator needs them
380
- registered_tools = self._get_runtime_tools(use_production_tools)
381
-
382
- # Retrieve available mcp_tools if the evaluator needs them
383
- mcp_tools = []
384
- if self.servers:
385
- from flock.core.flock_registry import get_registry
386
-
387
- FlockRegistry = get_registry() # Get the registry
388
- for server in self.servers:
389
- registered_server: FlockMCPServerBase | None = None
390
- server_tools = []
391
- if isinstance(server, FlockMCPServerBase):
392
- # check if registered
393
- server_name = server.config.name
394
- registered_server = FlockRegistry.get_server(
395
- server_name
396
- )
397
- else:
398
- # servers must be registered.
399
- registered_server = FlockRegistry.get_server(
400
- name=server
401
- )
402
- if registered_server:
403
- server_tools = await registered_server.get_tools(
404
- agent_id=self.agent_id,
405
- run_id=self.context.run_id,
406
- )
407
- else:
408
- logger.warning(
409
- f"No Server with name '{server.config.name}' registered! Skipping."
410
- )
411
- mcp_tools = mcp_tools + server_tools
412
-
413
- # --------------------------------------------------
414
- # Optional DI middleware pipeline
415
- # --------------------------------------------------
416
- container = None
417
- if self.context is not None:
418
- container = self.context.get_variable("di.container")
419
-
420
- # If a MiddlewarePipeline is registered in DI, wrap the evaluator
421
- result: dict[str, Any] | None = None
422
-
423
- if container is not None:
424
- try:
425
- from wd.di.middleware import (
426
- MiddlewarePipeline,
427
- )
428
-
429
- pipeline: MiddlewarePipeline | None = None
430
- try:
431
- pipeline = container.get_service(MiddlewarePipeline)
432
- except Exception:
433
- pipeline = None
434
-
435
- if pipeline is not None:
436
- # Build execution chain where the evaluator is the terminal handler
437
-
438
- async def _final_handler():
439
- return await self.evaluator.evaluate(
440
- self,
441
- current_inputs,
442
- registered_tools,
443
- mcp_tools=mcp_tools,
444
- )
445
-
446
- idx = 0
447
-
448
- async def _invoke_next():
449
- nonlocal idx
450
-
451
- if idx < len(pipeline._middleware):
452
- mw = pipeline._middleware[idx]
453
- idx += 1
454
- return await mw(self.context, _invoke_next) # type: ignore[arg-type]
455
- return await _final_handler()
456
-
457
- # Execute pipeline
458
- result = await _invoke_next()
459
- else:
460
- # No pipeline registered, direct evaluation
461
- result = await self.evaluator.evaluate(
462
- self,
463
- current_inputs,
464
- registered_tools,
465
- mcp_tools=mcp_tools,
466
- )
467
- except ImportError:
468
- # wd.di not installed – fall back
469
- result = await self.evaluator.evaluate(
470
- self,
471
- current_inputs,
472
- registered_tools,
473
- mcp_tools=mcp_tools,
474
- )
475
- else:
476
- # No DI container – standard execution
477
- result = await self.evaluator.evaluate(
478
- self,
479
- current_inputs,
480
- registered_tools,
481
- mcp_tools=mcp_tools,
482
- )
483
- except Exception as eval_error:
484
- logger.error(
485
- "Error during evaluate",
486
- agent=self.name,
487
- error=str(eval_error),
488
- )
489
- span.record_exception(eval_error)
490
- await self.on_error(
491
- eval_error, current_inputs
492
- ) # Call error hook
493
- raise # Re-raise the exception
494
-
495
- # Post-evaluate hooks
496
- current_result = result
497
- for module in self.get_enabled_modules():
498
- tmp_result = await module.on_post_evaluate(
499
- self,
500
- current_inputs,
501
- self.context,
502
- current_result,
503
- )
504
- # If the module returns a result, use it
505
- if tmp_result:
506
- current_result = tmp_result
507
-
508
- logger.debug(f"Evaluation completed for agent '{self.name}'")
509
- return current_result
510
-
511
- def run(
512
- self, inputs: dict[str, Any], *, use_production_tools: bool | None = None
513
- ) -> dict[str, Any]:
514
- """Synchronous wrapper for run_async."""
515
- try:
516
- loop = asyncio.get_running_loop()
517
- except (
518
- RuntimeError
519
- ): # 'RuntimeError: There is no current event loop...'
520
- loop = asyncio.new_event_loop()
521
- asyncio.set_event_loop(loop)
522
- return loop.run_until_complete(
523
- self.run_async(inputs, use_production_tools=use_production_tools)
524
- )
525
-
526
- def set_model(self, model: str):
527
- """Set the model for the agent and its evaluator."""
528
- self.model = model
529
- if self.evaluator and hasattr(self.evaluator, "config"):
530
- self.evaluator.config.model = model
531
- logger.info(
532
- f"Set model to '{model}' for agent '{self.name}' and its evaluator."
533
- )
534
- elif self.evaluator:
535
- logger.warning(
536
- f"Evaluator for agent '{self.name}' does not have a standard config to set model."
537
- )
538
- else:
539
- logger.warning(
540
- f"Agent '{self.name}' has no evaluator to set model for."
541
- )
542
-
543
- async def run_async(
544
- self, inputs: dict[str, Any], *, use_production_tools: bool | None = None
545
- ) -> dict[str, Any]:
546
- """Asynchronous execution logic with lifecycle hooks."""
547
- with tracer.start_as_current_span("agent.run") as span:
548
- span.set_attribute("agent.name", self.name)
549
- span.set_attribute("inputs", str(inputs))
550
- try:
551
- await self.initialize(inputs)
552
- result = await self.evaluate(
553
- inputs, use_production_tools=use_production_tools
554
- )
555
- await self.terminate(inputs, result)
556
- span.set_attribute("result", str(result))
557
- logger.info("Agent run completed", agent=self.name)
558
- return result
559
- except Exception as run_error:
560
- logger.error(
561
- "Error running agent", agent=self.name, error=str(run_error)
562
- )
563
- if "evaluate" not in str(
564
- run_error
565
- ): # Simple check, might need refinement
566
- await self.on_error(run_error, inputs)
567
- logger.error(
568
- f"Agent '{self.name}' run failed: {run_error}",
569
- exc_info=True,
570
- )
571
- span.record_exception(run_error)
572
- raise # Re-raise after handling
573
-
574
- async def run_temporal(self, inputs: dict[str, Any]) -> dict[str, Any]:
575
- with tracer.start_as_current_span("agent.run_temporal") as span:
576
- span.set_attribute("agent.name", self.name)
577
- span.set_attribute("inputs", str(inputs))
578
- try:
579
- from temporalio.client import Client
580
-
581
- from flock.workflow.agent_activities import (
582
- run_flock_agent_activity,
583
- )
584
- from flock.workflow.temporal_setup import run_activity
585
-
586
- client = await Client.connect(
587
- "localhost:7233", namespace="default"
588
- )
589
- agent_data = self.to_dict()
590
- inputs_data = inputs
591
-
592
- result = await run_activity(
593
- client,
594
- self.name,
595
- run_flock_agent_activity,
596
- {"agent_data": agent_data, "inputs": inputs_data},
597
- )
598
- span.set_attribute("result", str(result))
599
- logger.info("Temporal run successful", agent=self.name)
600
- return result
601
- except Exception as temporal_error:
602
- logger.error(
603
- "Error in Temporal workflow",
604
- agent=self.name,
605
- error=str(temporal_error),
606
- )
607
- span.record_exception(temporal_error)
608
- raise
609
-
610
- def add_component(
611
- self,
612
- config_instance: FlockModuleConfig
613
- | FlockRouterConfig
614
- | FlockEvaluatorConfig,
615
- component_name: str | None = None,
616
- ) -> "FlockAgent":
617
- """Adds or replaces a component (Evaluator, Router, Module) based on its configuration object.
618
-
619
- Args:
620
- config_instance: An instance of a config class inheriting from
621
- FlockModuleConfig, FlockRouterConfig, or FlockEvaluatorConfig.
622
- component_name: Explicit name for the component (required for Modules if not in config).
623
-
624
- Returns:
625
- self for potential chaining.
626
- """
627
- from flock.core.flock_registry import get_registry
628
-
629
- config_type = type(config_instance)
630
- registry = get_registry() # Get registry instance
631
- logger.debug(
632
- f"Attempting to add component via config: {config_type.__name__}"
633
- )
634
-
635
- # --- 1. Find Component Class using Registry Map ---
636
- ComponentClass = registry.get_component_class_for_config(config_type)
637
-
638
- if not ComponentClass:
639
- logger.error(
640
- f"No component class registered for config type {config_type.__name__}. Use @flock_component(config_class=...) on the component."
641
- )
642
- raise TypeError(
643
- f"Cannot find component class for config {config_type.__name__}"
644
- )
645
-
646
- component_class_name = ComponentClass.__name__
647
- logger.debug(
648
- f"Found component class '{component_class_name}' mapped to config '{config_type.__name__}'"
649
- )
650
-
651
- # --- 2. Determine Assignment Target and Name (Same as before) ---
652
- instance_name = component_name
653
- attribute_name: str = ""
654
-
655
- if issubclass(ComponentClass, FlockEvaluator):
656
- attribute_name = "evaluator"
657
- if not instance_name:
658
- instance_name = getattr(
659
- config_instance, "name", component_class_name.lower()
660
- )
661
-
662
- elif issubclass(ComponentClass, FlockRouter):
663
- attribute_name = "handoff_router"
664
- if not instance_name:
665
- instance_name = getattr(
666
- config_instance, "name", component_class_name.lower()
667
- )
668
-
669
- elif issubclass(ComponentClass, FlockModule):
670
- attribute_name = "modules"
671
- if not instance_name:
672
- instance_name = getattr(
673
- config_instance, "name", component_class_name.lower()
674
- )
675
- if not instance_name:
676
- raise ValueError(
677
- "Module name must be provided either in config or as component_name argument."
678
- )
679
- # Ensure config has name if module expects it
680
- if hasattr(config_instance, "name") and not getattr(
681
- config_instance, "name", None
682
- ):
683
- setattr(config_instance, "name", instance_name)
684
-
685
- else: # Should be caught by registry map logic ideally
686
- raise TypeError(
687
- f"Class '{component_class_name}' mapped from config is not a valid Flock component."
688
- )
689
-
690
- # --- 3. Instantiate the Component (Same as before) ---
691
- try:
692
- init_args = {"config": config_instance, "name": instance_name}
693
-
694
- component_instance = ComponentClass(**init_args)
695
- except Exception as e:
696
- logger.error(
697
- f"Failed to instantiate {ComponentClass.__name__} with config {config_type.__name__}: {e}",
698
- exc_info=True,
699
- )
700
- raise RuntimeError(f"Component instantiation failed: {e}") from e
701
-
702
- # --- 4. Assign to the Agent (Same as before) ---
703
- if attribute_name == "modules":
704
- if not isinstance(self.modules, dict):
705
- self.modules = {}
706
- self.modules[instance_name] = component_instance
707
- logger.info(
708
- f"Added/Updated module '{instance_name}' (type: {ComponentClass.__name__}) to agent '{self.name}'"
709
- )
710
- else:
711
- setattr(self, attribute_name, component_instance)
712
- logger.info(
713
- f"Set {attribute_name} to {ComponentClass.__name__} (instance name: '{instance_name}') for agent '{self.name}'"
714
- )
715
-
716
- return self
717
-
718
- # resolve_callables remains useful for dynamic definitions
719
- def resolve_callables(self, context: FlockContext | None = None) -> None:
720
- """Resolves callable fields (description, input, output) using context."""
721
- if callable(self.description):
722
- self.description = self.description(
723
- context
724
- ) # Pass context if needed by callable
725
- if callable(self.input):
726
- self.input = self.input(context)
727
- if callable(self.output):
728
- self.output = self.output(context)
729
-
730
- # --- Serialization Implementation ---
731
-
732
- def _save_output(self, agent_name: str, result: dict[str, Any]) -> None:
733
- """Save output to file if configured."""
734
- if not self.write_to_file:
735
- return
736
-
737
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
738
- filename = f"{agent_name}_output_{timestamp}.json"
739
- filepath = os.path.join(".flock/output/", filename)
740
- os.makedirs(".flock/output/", exist_ok=True)
741
-
742
- output_data = {
743
- "agent": agent_name,
744
- "timestamp": timestamp,
745
- "output": result,
746
- }
747
-
748
- try:
749
- with open(filepath, "w") as f:
750
- json.dump(output_data, f, indent=2, cls=FlockJSONEncoder)
751
- except Exception as e:
752
- logger.warning(f"Failed to save output to file: {e}")
753
-
754
- def to_dict(self) -> dict[str, Any]:
755
- """Convert instance to dictionary representation suitable for serialization."""
756
- from flock.core.flock_registry import get_registry
757
-
758
- FlockRegistry = get_registry()
759
-
760
- exclude = [
761
- "context",
762
- "evaluator",
763
- "modules",
764
- "handoff_router",
765
- "tools",
766
- "production_tools",
767
- "servers",
768
- ]
769
-
770
- is_descrition_callable = False
771
- is_input_callable = False
772
- is_output_callable = False
773
-
774
- # if self.description is a callable, exclude it
775
- if callable(self.description):
776
- is_descrition_callable = True
777
- exclude.append("description")
778
- # if self.input is a callable, exclude it
779
- if callable(self.input):
780
- is_input_callable = True
781
- exclude.append("input")
782
- # if self.output is a callable, exclude it
783
- if callable(self.output):
784
- is_output_callable = True
785
- exclude.append("output")
786
-
787
- logger.debug(f"Serializing agent '{self.name}' to dict.")
788
- # Use Pydantic's dump, exclude manually handled fields and runtime context
789
- data = self.model_dump(
790
- exclude=exclude,
791
- mode="json", # Use json mode for better handling of standard types by Pydantic
792
- exclude_none=True, # Exclude None values for cleaner output
793
- )
794
- logger.debug(f"Base agent data for '{self.name}': {list(data.keys())}")
795
- serialized_modules = {}
796
-
797
- def add_serialized_component(component: Any, field_name: str):
798
- if component:
799
- comp_type = type(component)
800
- type_name = FlockRegistry.get_component_type_name(
801
- comp_type
802
- ) # Get registered name
803
- if type_name:
804
- try:
805
- serialized_component_data = serialize_item(component)
806
-
807
- if not isinstance(serialized_component_data, dict):
808
- logger.error(
809
- f"Serialization of component {type_name} for field '{field_name}' did not result in a dictionary. Got: {type(serialized_component_data)}"
810
- )
811
- serialized_modules[field_name] = {
812
- "type": type_name,
813
- "name": getattr(component, "name", "unknown"),
814
- "error": "serialization_failed_non_dict",
815
- }
816
- else:
817
- serialized_component_data["type"] = type_name
818
- serialized_modules[field_name] = (
819
- serialized_component_data
820
- )
821
- logger.debug(
822
- f"Successfully serialized component for field '{field_name}' (type: {type_name})"
823
- )
824
-
825
- except Exception as e:
826
- logger.error(
827
- f"Failed to serialize component {type_name} for field '{field_name}': {e}",
828
- exc_info=True,
829
- )
830
- serialized_modules[field_name] = {
831
- "type": type_name,
832
- "name": getattr(component, "name", "unknown"),
833
- "error": "serialization_failed",
834
- }
835
- else:
836
- logger.warning(
837
- f"Cannot serialize unregistered component {comp_type.__name__} for field '{field_name}'"
838
- )
839
-
840
- add_serialized_component(self.evaluator, "evaluator")
841
- if serialized_modules:
842
- data["evaluator"] = serialized_modules["evaluator"]
843
- logger.debug(f"Added evaluator to agent '{self.name}'")
844
-
845
- serialized_modules = {}
846
- add_serialized_component(self.handoff_router, "handoff_router")
847
- if serialized_modules:
848
- data["handoff_router"] = serialized_modules["handoff_router"]
849
- logger.debug(f"Added handoff_router to agent '{self.name}'")
850
-
851
- serialized_modules = {}
852
- for module in self.modules.values():
853
- add_serialized_component(module, module.name)
854
-
855
- if serialized_modules:
856
- data["modules"] = serialized_modules
857
- logger.debug(
858
- f"Added {len(serialized_modules)} modules to agent '{self.name}'"
859
- )
860
-
861
- # --- Serialize Servers ---
862
- if self.servers:
863
- logger.debug(
864
- f"Serializing {len(self.servers)} servers for agent '{self.name}'"
865
- )
866
- serialized_servers = []
867
- for server in self.servers:
868
- if isinstance(server, FlockMCPServerBase):
869
- serialized_servers.append(server.config.name)
870
- else:
871
- # Write it down as a list of server names.
872
- serialized_servers.append(server)
873
-
874
- if serialized_servers:
875
- data["mcp_servers"] = serialized_servers
876
- logger.debug(
877
- f"Added {len(serialized_servers)} servers to agent '{self.name}'"
878
- )
879
-
880
- # --- Serialize Tools (Callables) ---
881
- if self.tools:
882
- logger.debug(
883
- f"Serializing {len(self.tools)} tools for agent '{self.name}'"
884
- )
885
- serialized_tools = []
886
- for tool in self.tools:
887
- if callable(tool) and not isinstance(tool, type):
888
- path_str = FlockRegistry.get_callable_path_string(tool)
889
- if path_str:
890
- # Get just the function name from the path string
891
- # If it's a namespaced path like module.submodule.function_name
892
- # Just use the function_name part
893
- func_name = path_str.split(".")[-1]
894
- serialized_tools.append(func_name)
895
- logger.debug(
896
- f"Added tool '{func_name}' (from path '{path_str}') to agent '{self.name}'"
897
- )
898
- else:
899
- logger.warning(
900
- f"Could not get path string for tool {tool} in agent '{self.name}'. Skipping."
901
- )
902
- else:
903
- logger.warning(
904
- f"Non-callable item found in tools list for agent '{self.name}': {tool}. Skipping."
905
- )
906
- if serialized_tools:
907
- data["tools"] = serialized_tools
908
- logger.debug(
909
- f"Added {len(serialized_tools)} tools to agent '{self.name}'"
910
- )
911
-
912
- if self.production_tools:
913
- logger.debug(
914
- f"Serializing {len(self.production_tools)} production tools for agent '{self.name}'"
915
- )
916
- serialized_prod_tools = []
917
- for tool in self.production_tools:
918
- if callable(tool) and not isinstance(tool, type):
919
- path_str = FlockRegistry.get_callable_path_string(tool)
920
- if path_str:
921
- func_name = path_str.split(".")[-1]
922
- serialized_prod_tools.append(func_name)
923
- logger.debug(
924
- f"Added production tool '{func_name}' (from path '{path_str}') to agent '{self.name}'"
925
- )
926
- else:
927
- logger.warning(
928
- f"Could not get path string for production tool {tool} in agent '{self.name}'. Skipping."
929
- )
930
- else:
931
- logger.warning(
932
- f"Non-callable item found in production tools list for agent '{self.name}': {tool}. Skipping."
933
- )
934
- if serialized_prod_tools:
935
- data["production_tools"] = serialized_prod_tools
936
- logger.debug(
937
- f"Added {len(serialized_prod_tools)} production tools to agent '{self.name}'"
938
- )
939
-
940
- if is_descrition_callable:
941
- path_str = FlockRegistry.get_callable_path_string(self.description)
942
- if path_str:
943
- func_name = path_str.split(".")[-1]
944
- data["description_callable"] = func_name
945
- logger.debug(
946
- f"Added description '{func_name}' (from path '{path_str}') to agent '{self.name}'"
947
- )
948
- else:
949
- logger.warning(
950
- f"Could not get path string for description {self.description} in agent '{self.name}'. Skipping."
951
- )
952
-
953
- if is_input_callable:
954
- path_str = FlockRegistry.get_callable_path_string(self.input)
955
- if path_str:
956
- func_name = path_str.split(".")[-1]
957
- data["input_callable"] = func_name
958
- logger.debug(
959
- f"Added input '{func_name}' (from path '{path_str}') to agent '{self.name}'"
960
- )
961
- else:
962
- logger.warning(
963
- f"Could not get path string for input {self.input} in agent '{self.name}'. Skipping."
964
- )
965
-
966
- if is_output_callable:
967
- path_str = FlockRegistry.get_callable_path_string(self.output)
968
- if path_str:
969
- func_name = path_str.split(".")[-1]
970
- data["output_callable"] = func_name
971
- logger.debug(
972
- f"Added output '{func_name}' (from path '{path_str}') to agent '{self.name}'"
973
- )
974
- else:
975
- logger.warning(
976
- f"Could not get path string for output {self.output} in agent '{self.name}'. Skipping."
977
- )
978
-
979
- # No need to call _filter_none_values here as model_dump(exclude_none=True) handles it
980
- logger.info(
981
- f"Serialization of agent '{self.name}' complete with {len(data)} fields"
982
- )
983
- return data
984
-
985
- @classmethod
986
- def from_dict(cls: type[T], data: dict[str, Any]) -> T:
987
- """Deserialize the agent from a dictionary, including components, tools, and callables."""
988
- from flock.core.flock_registry import (
989
- get_registry, # Import registry locally
990
- )
991
-
992
- registry = get_registry()
993
- logger.debug(
994
- f"Deserializing agent from dict. Keys: {list(data.keys())}"
995
- )
996
-
997
- # --- Separate Data ---
998
- component_configs = {}
999
- callable_configs = {}
1000
- tool_config = []
1001
- production_tool_config = []
1002
- servers_config = []
1003
- agent_data = {}
1004
-
1005
- component_keys = [
1006
- "evaluator",
1007
- "handoff_router",
1008
- "modules",
1009
- "temporal_activity_config",
1010
- ]
1011
- callable_keys = [
1012
- "description_callable",
1013
- "input_callable",
1014
- "output_callable",
1015
- ]
1016
- tool_key = "tools"
1017
- production_tool_key = "production_tools"
1018
-
1019
- servers_key = "mcp_servers"
1020
-
1021
- for key, value in data.items():
1022
- if key in component_keys and value is not None:
1023
- component_configs[key] = value
1024
- elif key in callable_keys and value is not None:
1025
- callable_configs[key] = value
1026
- elif key == tool_key and value is not None:
1027
- tool_config = value # Expecting a list of names
1028
- elif key == production_tool_key and value is not None:
1029
- production_tool_config = value
1030
- elif key == servers_key and value is not None:
1031
- servers_config = value # Expecting a list of names
1032
- elif key not in component_keys + callable_keys + [
1033
- tool_key,
1034
- production_tool_key,
1035
- servers_key,
1036
- ]: # Avoid double adding
1037
- agent_data[key] = value
1038
- # else: ignore keys that are None or already handled
1039
-
1040
- # --- Deserialize Base Agent ---
1041
- # Ensure required fields like 'name' are present if needed by __init__
1042
- if "name" not in agent_data:
1043
- raise ValueError(
1044
- "Agent data must include a 'name' field for deserialization."
1045
- )
1046
- agent_name_log = agent_data["name"] # For logging
1047
- logger.info(f"Deserializing base agent data for '{agent_name_log}'")
1048
-
1049
- # Pydantic should handle base fields based on type hints in __init__
1050
- agent = cls(**agent_data)
1051
- logger.debug(f"Base agent '{agent.name}' instantiated.")
1052
-
1053
- # --- Deserialize Components ---
1054
- logger.debug(f"Deserializing components for '{agent.name}'")
1055
- # Evaluator
1056
- if "evaluator" in component_configs:
1057
- try:
1058
- agent.evaluator = deserialize_component(
1059
- component_configs["evaluator"], FlockEvaluator
1060
- )
1061
- logger.debug(f"Deserialized evaluator for '{agent.name}'")
1062
- except Exception as e:
1063
- logger.error(
1064
- f"Failed to deserialize evaluator for '{agent.name}': {e}",
1065
- exc_info=True,
1066
- )
1067
-
1068
- # Handoff Router
1069
- if "handoff_router" in component_configs:
1070
- try:
1071
- agent.handoff_router = deserialize_component(
1072
- component_configs["handoff_router"], FlockRouter
1073
- )
1074
- logger.debug(f"Deserialized handoff_router for '{agent.name}'")
1075
- except Exception as e:
1076
- logger.error(
1077
- f"Failed to deserialize handoff_router for '{agent.name}': {e}",
1078
- exc_info=True,
1079
- )
1080
-
1081
- # Modules
1082
- if "modules" in component_configs:
1083
- agent.modules = {} # Initialize
1084
- for module_name, module_data in component_configs[
1085
- "modules"
1086
- ].items():
1087
- try:
1088
- module_instance = deserialize_component(
1089
- module_data, FlockModule
1090
- )
1091
- if module_instance:
1092
- # Use add_module for potential logic within it
1093
- agent.add_module(module_instance)
1094
- logger.debug(
1095
- f"Deserialized and added module '{module_name}' for '{agent.name}'"
1096
- )
1097
- except Exception as e:
1098
- logger.error(
1099
- f"Failed to deserialize module '{module_name}' for '{agent.name}': {e}",
1100
- exc_info=True,
1101
- )
1102
-
1103
- # Temporal Activity Config
1104
- if "temporal_activity_config" in component_configs:
1105
- try:
1106
- agent.temporal_activity_config = TemporalActivityConfig(
1107
- **component_configs["temporal_activity_config"]
1108
- )
1109
- logger.debug(
1110
- f"Deserialized temporal_activity_config for '{agent.name}'"
1111
- )
1112
- except Exception as e:
1113
- logger.error(
1114
- f"Failed to deserialize temporal_activity_config for '{agent.name}': {e}",
1115
- exc_info=True,
1116
- )
1117
- agent.temporal_activity_config = None
1118
-
1119
- # --- Deserialize Tools ---
1120
- agent.tools = [] # Initialize tools list
1121
- if tool_config:
1122
- logger.debug(
1123
- f"Deserializing {len(tool_config)} tools for '{agent.name}'"
1124
- )
1125
- # Use get_callable to find each tool
1126
- for tool_name_or_path in tool_config:
1127
- try:
1128
- found_tool = registry.get_callable(tool_name_or_path)
1129
- if found_tool and callable(found_tool):
1130
- agent.tools.append(found_tool)
1131
- logger.debug(
1132
- f"Resolved and added tool '{tool_name_or_path}' for agent '{agent.name}'"
1133
- )
1134
- else:
1135
- # Should not happen if get_callable returns successfully but just in case
1136
- logger.warning(
1137
- f"Registry returned non-callable for tool '{tool_name_or_path}' for agent '{agent.name}'. Skipping."
1138
- )
1139
- except (
1140
- ValueError
1141
- ) as e: # get_callable raises ValueError if not found/ambiguous
1142
- logger.warning(
1143
- f"Could not resolve tool '{tool_name_or_path}' for agent '{agent.name}': {e}. Skipping."
1144
- )
1145
- except Exception as e:
1146
- logger.error(
1147
- f"Unexpected error resolving tool '{tool_name_or_path}' for agent '{agent.name}': {e}. Skipping.",
1148
- exc_info=True,
1149
- )
1150
-
1151
- agent.production_tools = []
1152
- if production_tool_config:
1153
- logger.debug(
1154
- f"Deserializing {len(production_tool_config)} production tools for '{agent.name}'"
1155
- )
1156
- for tool_name_or_path in production_tool_config:
1157
- try:
1158
- found_tool = registry.get_callable(tool_name_or_path)
1159
- if found_tool and callable(found_tool):
1160
- agent.production_tools.append(found_tool)
1161
- logger.debug(
1162
- f"Resolved and added production tool '{tool_name_or_path}' for agent '{agent.name}'"
1163
- )
1164
- else:
1165
- logger.warning(
1166
- f"Registry returned non-callable for production tool '{tool_name_or_path}' for agent '{agent.name}'. Skipping."
1167
- )
1168
- except (ValueError) as e:
1169
- logger.warning(
1170
- f"Could not resolve production tool '{tool_name_or_path}' for agent '{agent.name}': {e}. Skipping."
1171
- )
1172
- except Exception as e:
1173
- logger.error(
1174
- f"Unexpected error resolving production tool '{tool_name_or_path}' for agent '{agent.name}': {e}. Skipping.",
1175
- exc_info=True,
1176
- )
1177
-
1178
- # --- Deserialize Servers ---
1179
- agent.servers = [] # Initialize Servers list.
1180
- if servers_config:
1181
- logger.debug(
1182
- f"Deserializing {len(servers_config)} servers for '{agent.name}'"
1183
- )
1184
- # Agents keep track of server by getting a list of server names.
1185
- # The server instances will be retrieved during runtime from the registry. (default behavior)
1186
-
1187
- for server_name in servers_config:
1188
- if isinstance(server_name, str):
1189
- # Case 1 (default behavior): A server name is passe.
1190
- agent.servers.append(server_name)
1191
- elif isinstance(server_name, FlockMCPServerBase):
1192
- # Case 2 (highly unlikely): If someone somehow manages to pass
1193
- # an instance of a server during the deserialization step (however that might be achieved)
1194
- # check the registry, if the server is already registered, if not, register it
1195
- # and store the name in the servers list
1196
- FlockRegistry = get_registry()
1197
- server_exists = (
1198
- FlockRegistry.get_server(server_name.config.name)
1199
- is not None
1200
- )
1201
- if server_exists:
1202
- agent.servers.append(server_name.config.name)
1203
- else:
1204
- FlockRegistry.register_server(
1205
- server=server_name
1206
- ) # register it.
1207
- agent.servers.append(server_name.config.name)
1208
-
1209
- # --- Deserialize Callables ---
1210
- logger.debug(f"Deserializing callable fields for '{agent.name}'")
1211
- # available_callables = registry.get_all_callables() # Incorrect
1212
-
1213
- def resolve_and_assign(field_name: str, callable_key: str):
1214
- if callable_key in callable_configs:
1215
- callable_name = callable_configs[callable_key]
1216
- try:
1217
- # Use get_callable to find the signature function
1218
- found_callable = registry.get_callable(callable_name)
1219
- if found_callable and callable(found_callable):
1220
- setattr(agent, field_name, found_callable)
1221
- logger.debug(
1222
- f"Resolved callable '{callable_name}' for field '{field_name}' on agent '{agent.name}'"
1223
- )
1224
- else:
1225
- logger.warning(
1226
- f"Registry returned non-callable for name '{callable_name}' for field '{field_name}' on agent '{agent.name}'. Field remains default."
1227
- )
1228
- except (
1229
- ValueError
1230
- ) as e: # get_callable raises ValueError if not found/ambiguous
1231
- logger.warning(
1232
- f"Could not resolve callable '{callable_name}' in registry for field '{field_name}' on agent '{agent.name}': {e}. Field remains default."
1233
- )
1234
- except Exception as e:
1235
- logger.error(
1236
- f"Unexpected error resolving callable '{callable_name}' for field '{field_name}' on agent '{agent.name}': {e}. Field remains default.",
1237
- exc_info=True,
1238
- )
1239
- # Else: key not present, field retains its default value from __init__
1240
-
1241
- resolve_and_assign("description", "description_callable")
1242
- resolve_and_assign("input", "input_callable")
1243
- resolve_and_assign("output", "output_callable")
1244
-
1245
- logger.info(f"Successfully deserialized agent '{agent.name}'.")
1246
- return agent
1247
-
1248
- # --- Pydantic v2 Configuration ---
1249
- class Config:
1250
- arbitrary_types_allowed = (
1251
- True # Important for components like evaluator, router etc.
1252
- )
1253
- # Might need custom json_encoders if not using model_dump(mode='json') everywhere
1254
- # json_encoders = {
1255
- # FlockEvaluator: lambda v: v.to_dict() if v else None,
1256
- # FlockRouter: lambda v: v.to_dict() if v else None,
1257
- # FlockModule: lambda v: v.to_dict() if v else None,
1258
- # }