flock-core 0.5.0b28__py3-none-any.whl → 0.5.0b51__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 (469) 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/frontend/README.md +678 -0
  17. flock/frontend/docs/DESIGN_SYSTEM.md +1980 -0
  18. flock/frontend/index.html +12 -0
  19. flock/frontend/package-lock.json +4347 -0
  20. flock/frontend/package.json +48 -0
  21. flock/frontend/src/App.tsx +79 -0
  22. flock/frontend/src/__tests__/e2e/critical-scenarios.test.tsx +587 -0
  23. flock/frontend/src/__tests__/integration/filtering-e2e.test.tsx +387 -0
  24. flock/frontend/src/__tests__/integration/graph-rendering.test.tsx +640 -0
  25. flock/frontend/src/__tests__/integration/indexeddb-persistence.test.tsx +699 -0
  26. flock/frontend/src/components/common/BuildInfo.tsx +39 -0
  27. flock/frontend/src/components/common/EmptyState.module.css +115 -0
  28. flock/frontend/src/components/common/EmptyState.tsx +128 -0
  29. flock/frontend/src/components/common/ErrorBoundary.module.css +169 -0
  30. flock/frontend/src/components/common/ErrorBoundary.tsx +118 -0
  31. flock/frontend/src/components/common/KeyboardShortcutsDialog.css +251 -0
  32. flock/frontend/src/components/common/KeyboardShortcutsDialog.tsx +151 -0
  33. flock/frontend/src/components/common/LoadingSpinner.module.css +97 -0
  34. flock/frontend/src/components/common/LoadingSpinner.tsx +29 -0
  35. flock/frontend/src/components/controls/PublishControl.css +547 -0
  36. flock/frontend/src/components/controls/PublishControl.test.tsx +543 -0
  37. flock/frontend/src/components/controls/PublishControl.tsx +432 -0
  38. flock/frontend/src/components/details/DetailWindowContainer.tsx +62 -0
  39. flock/frontend/src/components/details/LiveOutputTab.test.tsx +792 -0
  40. flock/frontend/src/components/details/LiveOutputTab.tsx +220 -0
  41. flock/frontend/src/components/details/MessageHistoryTab.tsx +299 -0
  42. flock/frontend/src/components/details/NodeDetailWindow.test.tsx +501 -0
  43. flock/frontend/src/components/details/NodeDetailWindow.tsx +218 -0
  44. flock/frontend/src/components/details/RunStatusTab.tsx +307 -0
  45. flock/frontend/src/components/details/tabs.test.tsx +1015 -0
  46. flock/frontend/src/components/filters/CorrelationIDFilter.module.css +102 -0
  47. flock/frontend/src/components/filters/CorrelationIDFilter.test.tsx +197 -0
  48. flock/frontend/src/components/filters/CorrelationIDFilter.tsx +121 -0
  49. flock/frontend/src/components/filters/FilterBar.module.css +29 -0
  50. flock/frontend/src/components/filters/FilterBar.test.tsx +133 -0
  51. flock/frontend/src/components/filters/FilterBar.tsx +33 -0
  52. flock/frontend/src/components/filters/FilterPills.module.css +79 -0
  53. flock/frontend/src/components/filters/FilterPills.test.tsx +173 -0
  54. flock/frontend/src/components/filters/FilterPills.tsx +67 -0
  55. flock/frontend/src/components/filters/TimeRangeFilter.module.css +91 -0
  56. flock/frontend/src/components/filters/TimeRangeFilter.test.tsx +154 -0
  57. flock/frontend/src/components/filters/TimeRangeFilter.tsx +105 -0
  58. flock/frontend/src/components/graph/AgentNode.test.tsx +75 -0
  59. flock/frontend/src/components/graph/AgentNode.tsx +322 -0
  60. flock/frontend/src/components/graph/GraphCanvas.tsx +406 -0
  61. flock/frontend/src/components/graph/MessageFlowEdge.tsx +128 -0
  62. flock/frontend/src/components/graph/MessageNode.test.tsx +62 -0
  63. flock/frontend/src/components/graph/MessageNode.tsx +116 -0
  64. flock/frontend/src/components/graph/MiniMap.tsx +47 -0
  65. flock/frontend/src/components/graph/TransformEdge.tsx +123 -0
  66. flock/frontend/src/components/layout/DashboardLayout.css +407 -0
  67. flock/frontend/src/components/layout/DashboardLayout.tsx +300 -0
  68. flock/frontend/src/components/layout/Header.module.css +88 -0
  69. flock/frontend/src/components/layout/Header.tsx +52 -0
  70. flock/frontend/src/components/modules/EventLogModule.test.tsx +401 -0
  71. flock/frontend/src/components/modules/EventLogModule.tsx +396 -0
  72. flock/frontend/src/components/modules/EventLogModuleWrapper.tsx +17 -0
  73. flock/frontend/src/components/modules/ModuleRegistry.test.ts +333 -0
  74. flock/frontend/src/components/modules/ModuleRegistry.ts +85 -0
  75. flock/frontend/src/components/modules/ModuleWindow.tsx +155 -0
  76. flock/frontend/src/components/modules/registerModules.ts +20 -0
  77. flock/frontend/src/components/settings/AdvancedSettings.tsx +175 -0
  78. flock/frontend/src/components/settings/AppearanceSettings.tsx +185 -0
  79. flock/frontend/src/components/settings/GraphSettings.tsx +110 -0
  80. flock/frontend/src/components/settings/SettingsPanel.css +327 -0
  81. flock/frontend/src/components/settings/SettingsPanel.tsx +131 -0
  82. flock/frontend/src/components/settings/ThemeSelector.tsx +298 -0
  83. flock/frontend/src/hooks/useKeyboardShortcuts.ts +148 -0
  84. flock/frontend/src/hooks/useModulePersistence.test.ts +442 -0
  85. flock/frontend/src/hooks/useModulePersistence.ts +154 -0
  86. flock/frontend/src/hooks/useModules.ts +139 -0
  87. flock/frontend/src/hooks/usePersistence.ts +139 -0
  88. flock/frontend/src/main.tsx +13 -0
  89. flock/frontend/src/services/api.ts +213 -0
  90. flock/frontend/src/services/indexeddb.test.ts +793 -0
  91. flock/frontend/src/services/indexeddb.ts +794 -0
  92. flock/frontend/src/services/layout.test.ts +437 -0
  93. flock/frontend/src/services/layout.ts +146 -0
  94. flock/frontend/src/services/themeApplicator.ts +140 -0
  95. flock/frontend/src/services/themeService.ts +77 -0
  96. flock/frontend/src/services/websocket.test.ts +595 -0
  97. flock/frontend/src/services/websocket.ts +685 -0
  98. flock/frontend/src/store/filterStore.test.ts +242 -0
  99. flock/frontend/src/store/filterStore.ts +103 -0
  100. flock/frontend/src/store/graphStore.test.ts +186 -0
  101. flock/frontend/src/store/graphStore.ts +414 -0
  102. flock/frontend/src/store/moduleStore.test.ts +253 -0
  103. flock/frontend/src/store/moduleStore.ts +57 -0
  104. flock/frontend/src/store/settingsStore.ts +188 -0
  105. flock/frontend/src/store/streamStore.ts +68 -0
  106. flock/frontend/src/store/uiStore.test.ts +54 -0
  107. flock/frontend/src/store/uiStore.ts +110 -0
  108. flock/frontend/src/store/wsStore.ts +34 -0
  109. flock/frontend/src/styles/index.css +15 -0
  110. flock/frontend/src/styles/scrollbar.css +47 -0
  111. flock/frontend/src/styles/variables.css +488 -0
  112. flock/frontend/src/test/setup.ts +1 -0
  113. flock/frontend/src/types/filters.ts +14 -0
  114. flock/frontend/src/types/graph.ts +55 -0
  115. flock/frontend/src/types/modules.ts +7 -0
  116. flock/frontend/src/types/theme.ts +55 -0
  117. flock/frontend/src/utils/mockData.ts +85 -0
  118. flock/frontend/src/utils/performance.ts +16 -0
  119. flock/frontend/src/utils/transforms.test.ts +860 -0
  120. flock/frontend/src/utils/transforms.ts +323 -0
  121. flock/frontend/src/vite-env.d.ts +17 -0
  122. flock/frontend/tsconfig.json +27 -0
  123. flock/frontend/tsconfig.node.json +11 -0
  124. flock/frontend/vite.config.ts +25 -0
  125. flock/frontend/vitest.config.ts +11 -0
  126. flock/{core/util → helper}/cli_helper.py +4 -3
  127. flock/{core/logging → logging}/__init__.py +2 -3
  128. flock/{core/logging → logging}/formatters/enum_builder.py +3 -4
  129. flock/{core/logging → logging}/formatters/theme_builder.py +19 -44
  130. flock/{core/logging → logging}/formatters/themed_formatter.py +69 -115
  131. flock/{core/logging → logging}/logging.py +77 -61
  132. flock/{core/logging → logging}/telemetry.py +20 -26
  133. flock/{core/logging → logging}/telemetry_exporter/base_exporter.py +2 -2
  134. flock/{core/logging → logging}/telemetry_exporter/file_exporter.py +6 -9
  135. flock/{core/logging → logging}/telemetry_exporter/sqlite_exporter.py +2 -3
  136. flock/{core/logging → logging}/trace_and_logged.py +20 -24
  137. flock/mcp/__init__.py +91 -0
  138. flock/{core/mcp/mcp_client.py → mcp/client.py} +103 -154
  139. flock/{core/mcp/mcp_config.py → mcp/config.py} +62 -117
  140. flock/mcp/manager.py +255 -0
  141. flock/mcp/servers/sse/__init__.py +1 -1
  142. flock/mcp/servers/sse/flock_sse_server.py +11 -53
  143. flock/mcp/servers/stdio/__init__.py +1 -1
  144. flock/mcp/servers/stdio/flock_stdio_server.py +8 -48
  145. flock/mcp/servers/streamable_http/flock_streamable_http_server.py +17 -62
  146. flock/mcp/servers/websockets/flock_websocket_server.py +7 -40
  147. flock/{core/mcp/flock_mcp_tool.py → mcp/tool.py} +16 -26
  148. flock/mcp/types/__init__.py +42 -0
  149. flock/{core/mcp → mcp}/types/callbacks.py +9 -15
  150. flock/{core/mcp → mcp}/types/factories.py +7 -6
  151. flock/{core/mcp → mcp}/types/handlers.py +13 -18
  152. flock/{core/mcp → mcp}/types/types.py +70 -74
  153. flock/{core/mcp → mcp}/util/helpers.py +1 -1
  154. flock/orchestrator.py +645 -0
  155. flock/registry.py +148 -0
  156. flock/runtime.py +262 -0
  157. flock/service.py +140 -0
  158. flock/store.py +69 -0
  159. flock/subscription.py +111 -0
  160. flock/themes/andromeda.toml +1 -1
  161. flock/themes/apple-system-colors.toml +1 -1
  162. flock/themes/arcoiris.toml +1 -1
  163. flock/themes/atomonelight.toml +1 -1
  164. flock/themes/ayu copy.toml +1 -1
  165. flock/themes/ayu-light.toml +1 -1
  166. flock/themes/belafonte-day.toml +1 -1
  167. flock/themes/belafonte-night.toml +1 -1
  168. flock/themes/blulocodark.toml +1 -1
  169. flock/themes/breeze.toml +1 -1
  170. flock/themes/broadcast.toml +1 -1
  171. flock/themes/brogrammer.toml +1 -1
  172. flock/themes/builtin-dark.toml +1 -1
  173. flock/themes/builtin-pastel-dark.toml +1 -1
  174. flock/themes/catppuccin-latte.toml +1 -1
  175. flock/themes/catppuccin-macchiato.toml +1 -1
  176. flock/themes/catppuccin-mocha.toml +1 -1
  177. flock/themes/cga.toml +1 -1
  178. flock/themes/chalk.toml +1 -1
  179. flock/themes/ciapre.toml +1 -1
  180. flock/themes/coffee-theme.toml +1 -1
  181. flock/themes/cyberpunkscarletprotocol.toml +1 -1
  182. flock/themes/dark+.toml +1 -1
  183. flock/themes/darkermatrix.toml +1 -1
  184. flock/themes/darkside.toml +1 -1
  185. flock/themes/desert.toml +1 -1
  186. flock/themes/django.toml +1 -1
  187. flock/themes/djangosmooth.toml +1 -1
  188. flock/themes/doomone.toml +1 -1
  189. flock/themes/dotgov.toml +1 -1
  190. flock/themes/dracula+.toml +1 -1
  191. flock/themes/duckbones.toml +1 -1
  192. flock/themes/encom.toml +1 -1
  193. flock/themes/espresso.toml +1 -1
  194. flock/themes/everblush.toml +1 -1
  195. flock/themes/fairyfloss.toml +1 -1
  196. flock/themes/fideloper.toml +1 -1
  197. flock/themes/fishtank.toml +1 -1
  198. flock/themes/flexoki-light.toml +1 -1
  199. flock/themes/floraverse.toml +1 -1
  200. flock/themes/framer.toml +1 -1
  201. flock/themes/galizur.toml +1 -1
  202. flock/themes/github.toml +1 -1
  203. flock/themes/grass.toml +1 -1
  204. flock/themes/grey-green.toml +1 -1
  205. flock/themes/gruvboxlight.toml +1 -1
  206. flock/themes/guezwhoz.toml +1 -1
  207. flock/themes/harper.toml +1 -1
  208. flock/themes/hax0r-blue.toml +1 -1
  209. flock/themes/hopscotch.256.toml +1 -1
  210. flock/themes/ic-green-ppl.toml +1 -1
  211. flock/themes/iceberg-dark.toml +1 -1
  212. flock/themes/japanesque.toml +1 -1
  213. flock/themes/jubi.toml +1 -1
  214. flock/themes/kibble.toml +1 -1
  215. flock/themes/kolorit.toml +1 -1
  216. flock/themes/kurokula.toml +1 -1
  217. flock/themes/materialdesigncolors.toml +1 -1
  218. flock/themes/matrix.toml +1 -1
  219. flock/themes/mellifluous.toml +1 -1
  220. flock/themes/midnight-in-mojave.toml +1 -1
  221. flock/themes/monokai-remastered.toml +1 -1
  222. flock/themes/monokai-soda.toml +1 -1
  223. flock/themes/neon.toml +1 -1
  224. flock/themes/neopolitan.toml +1 -1
  225. flock/themes/nord-light.toml +1 -1
  226. flock/themes/ocean.toml +1 -1
  227. flock/themes/onehalfdark.toml +1 -1
  228. flock/themes/onehalflight.toml +1 -1
  229. flock/themes/palenighthc.toml +1 -1
  230. flock/themes/paulmillr.toml +1 -1
  231. flock/themes/pencildark.toml +1 -1
  232. flock/themes/pnevma.toml +1 -1
  233. flock/themes/purple-rain.toml +1 -1
  234. flock/themes/purplepeter.toml +1 -1
  235. flock/themes/raycast-dark.toml +1 -1
  236. flock/themes/red-sands.toml +1 -1
  237. flock/themes/relaxed.toml +1 -1
  238. flock/themes/retro.toml +1 -1
  239. flock/themes/rose-pine.toml +1 -1
  240. flock/themes/royal.toml +1 -1
  241. flock/themes/ryuuko.toml +1 -1
  242. flock/themes/sakura.toml +1 -1
  243. flock/themes/scarlet-protocol.toml +1 -1
  244. flock/themes/seoulbones-dark.toml +1 -1
  245. flock/themes/shades-of-purple.toml +1 -1
  246. flock/themes/smyck.toml +1 -1
  247. flock/themes/softserver.toml +1 -1
  248. flock/themes/solarized-darcula.toml +1 -1
  249. flock/themes/square.toml +1 -1
  250. flock/themes/sugarplum.toml +1 -1
  251. flock/themes/thayer-bright.toml +1 -1
  252. flock/themes/tokyonight.toml +1 -1
  253. flock/themes/tomorrow.toml +1 -1
  254. flock/themes/ubuntu.toml +1 -1
  255. flock/themes/ultradark.toml +1 -1
  256. flock/themes/ultraviolent.toml +1 -1
  257. flock/themes/unikitty.toml +1 -1
  258. flock/themes/urple.toml +1 -1
  259. flock/themes/vesper.toml +1 -1
  260. flock/themes/vimbones.toml +1 -1
  261. flock/themes/wildcherry.toml +1 -1
  262. flock/themes/wilmersdorf.toml +1 -1
  263. flock/themes/wryan.toml +1 -1
  264. flock/themes/xcodedarkhc.toml +1 -1
  265. flock/themes/xcodelight.toml +1 -1
  266. flock/themes/zenbones-light.toml +1 -1
  267. flock/themes/zenwritten-dark.toml +1 -1
  268. flock/utilities.py +301 -0
  269. flock/{components/utility → utility}/output_utility_component.py +68 -53
  270. flock/visibility.py +107 -0
  271. flock_core-0.5.0b51.dist-info/METADATA +747 -0
  272. flock_core-0.5.0b51.dist-info/RECORD +508 -0
  273. flock_core-0.5.0b51.dist-info/entry_points.txt +2 -0
  274. {flock_core-0.5.0b28.dist-info → flock_core-0.5.0b51.dist-info}/licenses/LICENSE +1 -1
  275. flock/adapter/__init__.py +0 -14
  276. flock/adapter/azure_adapter.py +0 -68
  277. flock/adapter/chroma_adapter.py +0 -73
  278. flock/adapter/faiss_adapter.py +0 -97
  279. flock/adapter/pinecone_adapter.py +0 -51
  280. flock/adapter/vector_base.py +0 -47
  281. flock/cli/assets/release_notes.md +0 -140
  282. flock/cli/config.py +0 -8
  283. flock/cli/constants.py +0 -36
  284. flock/cli/create_agent.py +0 -1
  285. flock/cli/create_flock.py +0 -280
  286. flock/cli/execute_flock.py +0 -620
  287. flock/cli/load_agent.py +0 -1
  288. flock/cli/load_examples.py +0 -1
  289. flock/cli/load_flock.py +0 -192
  290. flock/cli/load_release_notes.py +0 -20
  291. flock/cli/loaded_flock_cli.py +0 -254
  292. flock/cli/manage_agents.py +0 -459
  293. flock/cli/registry_management.py +0 -889
  294. flock/cli/runner.py +0 -41
  295. flock/cli/settings.py +0 -857
  296. flock/cli/utils.py +0 -135
  297. flock/cli/view_results.py +0 -29
  298. flock/cli/yaml_editor.py +0 -396
  299. flock/components/__init__.py +0 -30
  300. flock/components/evaluation/__init__.py +0 -9
  301. flock/components/evaluation/declarative_evaluation_component.py +0 -606
  302. flock/components/routing/__init__.py +0 -15
  303. flock/components/routing/conditional_routing_component.py +0 -494
  304. flock/components/routing/default_routing_component.py +0 -103
  305. flock/components/routing/llm_routing_component.py +0 -206
  306. flock/components/utility/__init__.py +0 -22
  307. flock/components/utility/example_utility_component.py +0 -250
  308. flock/components/utility/feedback_utility_component.py +0 -206
  309. flock/components/utility/memory_utility_component.py +0 -550
  310. flock/components/utility/metrics_utility_component.py +0 -700
  311. flock/config.py +0 -61
  312. flock/core/__init__.py +0 -110
  313. flock/core/agent/__init__.py +0 -16
  314. flock/core/agent/default_agent.py +0 -216
  315. flock/core/agent/flock_agent_components.py +0 -104
  316. flock/core/agent/flock_agent_execution.py +0 -101
  317. flock/core/agent/flock_agent_integration.py +0 -260
  318. flock/core/agent/flock_agent_lifecycle.py +0 -186
  319. flock/core/agent/flock_agent_serialization.py +0 -381
  320. flock/core/api/__init__.py +0 -10
  321. flock/core/api/custom_endpoint.py +0 -45
  322. flock/core/api/endpoints.py +0 -254
  323. flock/core/api/main.py +0 -162
  324. flock/core/api/models.py +0 -97
  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/component/__init__.py +0 -15
  329. flock/core/component/agent_component_base.py +0 -309
  330. flock/core/component/evaluation_component.py +0 -62
  331. flock/core/component/routing_component.py +0 -74
  332. flock/core/component/utility_component.py +0 -69
  333. flock/core/config/flock_agent_config.py +0 -58
  334. flock/core/config/scheduled_agent_config.py +0 -40
  335. flock/core/context/context.py +0 -213
  336. flock/core/context/context_manager.py +0 -37
  337. flock/core/context/context_vars.py +0 -10
  338. flock/core/evaluation/utils.py +0 -396
  339. flock/core/execution/batch_executor.py +0 -369
  340. flock/core/execution/evaluation_executor.py +0 -438
  341. flock/core/execution/local_executor.py +0 -31
  342. flock/core/execution/opik_executor.py +0 -103
  343. flock/core/execution/temporal_executor.py +0 -164
  344. flock/core/flock.py +0 -634
  345. flock/core/flock_agent.py +0 -336
  346. flock/core/flock_factory.py +0 -613
  347. flock/core/flock_scheduler.py +0 -166
  348. flock/core/flock_server_manager.py +0 -136
  349. flock/core/interpreter/python_interpreter.py +0 -689
  350. flock/core/mcp/__init__.py +0 -1
  351. flock/core/mcp/flock_mcp_server.py +0 -680
  352. flock/core/mcp/mcp_client_manager.py +0 -201
  353. flock/core/mcp/types/__init__.py +0 -1
  354. flock/core/mixin/dspy_integration.py +0 -403
  355. flock/core/mixin/prompt_parser.py +0 -125
  356. flock/core/orchestration/__init__.py +0 -15
  357. flock/core/orchestration/flock_batch_processor.py +0 -94
  358. flock/core/orchestration/flock_evaluator.py +0 -113
  359. flock/core/orchestration/flock_execution.py +0 -295
  360. flock/core/orchestration/flock_initialization.py +0 -149
  361. flock/core/orchestration/flock_server_manager.py +0 -67
  362. flock/core/orchestration/flock_web_server.py +0 -117
  363. flock/core/registry/__init__.py +0 -45
  364. flock/core/registry/agent_registry.py +0 -69
  365. flock/core/registry/callable_registry.py +0 -139
  366. flock/core/registry/component_discovery.py +0 -142
  367. flock/core/registry/component_registry.py +0 -64
  368. flock/core/registry/config_mapping.py +0 -64
  369. flock/core/registry/decorators.py +0 -137
  370. flock/core/registry/registry_hub.py +0 -205
  371. flock/core/registry/server_registry.py +0 -57
  372. flock/core/registry/type_registry.py +0 -86
  373. flock/core/serialization/__init__.py +0 -13
  374. flock/core/serialization/callable_registry.py +0 -52
  375. flock/core/serialization/flock_serializer.py +0 -832
  376. flock/core/serialization/json_encoder.py +0 -41
  377. flock/core/serialization/secure_serializer.py +0 -175
  378. flock/core/serialization/serializable.py +0 -342
  379. flock/core/serialization/serialization_utils.py +0 -412
  380. flock/core/util/file_path_utils.py +0 -223
  381. flock/core/util/hydrator.py +0 -309
  382. flock/core/util/input_resolver.py +0 -164
  383. flock/core/util/loader.py +0 -59
  384. flock/core/util/splitter.py +0 -219
  385. flock/di.py +0 -27
  386. flock/platform/docker_tools.py +0 -49
  387. flock/platform/jaeger_install.py +0 -86
  388. flock/webapp/__init__.py +0 -1
  389. flock/webapp/app/__init__.py +0 -0
  390. flock/webapp/app/api/__init__.py +0 -0
  391. flock/webapp/app/api/agent_management.py +0 -241
  392. flock/webapp/app/api/execution.py +0 -709
  393. flock/webapp/app/api/flock_management.py +0 -129
  394. flock/webapp/app/api/registry_viewer.py +0 -30
  395. flock/webapp/app/chat.py +0 -665
  396. flock/webapp/app/config.py +0 -104
  397. flock/webapp/app/dependencies.py +0 -117
  398. flock/webapp/app/main.py +0 -1070
  399. flock/webapp/app/middleware.py +0 -113
  400. flock/webapp/app/models_ui.py +0 -7
  401. flock/webapp/app/services/__init__.py +0 -0
  402. flock/webapp/app/services/feedback_file_service.py +0 -363
  403. flock/webapp/app/services/flock_service.py +0 -337
  404. flock/webapp/app/services/sharing_models.py +0 -81
  405. flock/webapp/app/services/sharing_store.py +0 -762
  406. flock/webapp/app/templates/theme_mapper.html +0 -326
  407. flock/webapp/app/theme_mapper.py +0 -812
  408. flock/webapp/app/utils.py +0 -85
  409. flock/webapp/run.py +0 -215
  410. flock/webapp/static/css/chat.css +0 -301
  411. flock/webapp/static/css/components.css +0 -167
  412. flock/webapp/static/css/header.css +0 -39
  413. flock/webapp/static/css/layout.css +0 -46
  414. flock/webapp/static/css/sidebar.css +0 -127
  415. flock/webapp/static/css/two-pane.css +0 -48
  416. flock/webapp/templates/base.html +0 -200
  417. flock/webapp/templates/chat.html +0 -152
  418. flock/webapp/templates/chat_settings.html +0 -19
  419. flock/webapp/templates/flock_editor.html +0 -16
  420. flock/webapp/templates/index.html +0 -12
  421. flock/webapp/templates/partials/_agent_detail_form.html +0 -93
  422. flock/webapp/templates/partials/_agent_list.html +0 -18
  423. flock/webapp/templates/partials/_agent_manager_view.html +0 -51
  424. flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
  425. flock/webapp/templates/partials/_chat_container.html +0 -15
  426. flock/webapp/templates/partials/_chat_messages.html +0 -57
  427. flock/webapp/templates/partials/_chat_settings_form.html +0 -85
  428. flock/webapp/templates/partials/_create_flock_form.html +0 -50
  429. flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
  430. flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
  431. flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
  432. flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
  433. flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
  434. flock/webapp/templates/partials/_env_vars_table.html +0 -23
  435. flock/webapp/templates/partials/_execution_form.html +0 -118
  436. flock/webapp/templates/partials/_execution_view_container.html +0 -28
  437. flock/webapp/templates/partials/_flock_file_list.html +0 -23
  438. flock/webapp/templates/partials/_flock_properties_form.html +0 -52
  439. flock/webapp/templates/partials/_flock_upload_form.html +0 -16
  440. flock/webapp/templates/partials/_header_flock_status.html +0 -5
  441. flock/webapp/templates/partials/_load_manager_view.html +0 -49
  442. flock/webapp/templates/partials/_registry_table.html +0 -25
  443. flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
  444. flock/webapp/templates/partials/_results_display.html +0 -78
  445. flock/webapp/templates/partials/_settings_env_content.html +0 -9
  446. flock/webapp/templates/partials/_settings_theme_content.html +0 -14
  447. flock/webapp/templates/partials/_settings_view.html +0 -36
  448. flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
  449. flock/webapp/templates/partials/_share_link_snippet.html +0 -35
  450. flock/webapp/templates/partials/_sidebar.html +0 -74
  451. flock/webapp/templates/partials/_streaming_results_container.html +0 -195
  452. flock/webapp/templates/partials/_structured_data_view.html +0 -40
  453. flock/webapp/templates/partials/_theme_preview.html +0 -36
  454. flock/webapp/templates/registry_viewer.html +0 -84
  455. flock/webapp/templates/shared_run_page.html +0 -140
  456. flock/workflow/__init__.py +0 -0
  457. flock/workflow/activities.py +0 -196
  458. flock/workflow/agent_activities.py +0 -24
  459. flock/workflow/agent_execution_activity.py +0 -202
  460. flock/workflow/flock_workflow.py +0 -214
  461. flock/workflow/temporal_config.py +0 -96
  462. flock/workflow/temporal_setup.py +0 -68
  463. flock_core-0.5.0b28.dist-info/METADATA +0 -274
  464. flock_core-0.5.0b28.dist-info/RECORD +0 -561
  465. flock_core-0.5.0b28.dist-info/entry_points.txt +0 -2
  466. /flock/{core/logging → logging}/formatters/themes.py +0 -0
  467. /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
  468. /flock/{core/mcp → mcp}/util/__init__.py +0 -0
  469. {flock_core-0.5.0b28.dist-info → flock_core-0.5.0b51.dist-info}/WHEEL +0 -0
@@ -0,0 +1,437 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { Node, Edge } from '@xyflow/react';
3
+
4
+ /**
5
+ * Phase 4: Graph Visualization & Dual Views - Layout Tests
6
+ *
7
+ * Tests for Dagre-based hierarchical layout algorithm.
8
+ * These tests validate layout generation, node positioning, performance,
9
+ * and edge routing capabilities.
10
+ *
11
+ * SPECIFICATION: docs/specs/003-real-time-dashboard/PLAN.md Phase 4
12
+ * REQUIREMENT: Auto-layout completes <200ms for 10 nodes
13
+ */
14
+
15
+ // Type definitions for the layout service (to be implemented)
16
+ interface LayoutOptions {
17
+ direction?: 'TB' | 'LR' | 'BT' | 'RL';
18
+ nodeSpacing?: number;
19
+ rankSpacing?: number;
20
+ }
21
+
22
+ interface LayoutResult {
23
+ nodes: Node[];
24
+ edges: Edge[];
25
+ width: number;
26
+ height: number;
27
+ }
28
+
29
+ // Mock layout service interface (implementation will be in layout.ts)
30
+ interface LayoutService {
31
+ applyHierarchicalLayout(nodes: Node[], edges: Edge[], options?: LayoutOptions): LayoutResult;
32
+ }
33
+
34
+ describe('Layout Service', () => {
35
+ // This will be replaced with actual implementation
36
+ let layoutService: LayoutService;
37
+
38
+ beforeEach(() => {
39
+ // Mock implementation for testing
40
+ layoutService = {
41
+ applyHierarchicalLayout: (_nodes, _edges, _options) => {
42
+ // Placeholder that will fail until real implementation
43
+ throw new Error('Layout service not implemented');
44
+ },
45
+ };
46
+ });
47
+
48
+ describe('Hierarchical Layout Generation', () => {
49
+ it('should generate hierarchical layout in vertical direction (TB)', () => {
50
+ const nodes: Node[] = [
51
+ { id: 'agent-1', type: 'agent', position: { x: 0, y: 0 }, data: { name: 'Agent 1' } },
52
+ { id: 'agent-2', type: 'agent', position: { x: 0, y: 0 }, data: { name: 'Agent 2' } },
53
+ { id: 'agent-3', type: 'agent', position: { x: 0, y: 0 }, data: { name: 'Agent 3' } },
54
+ ];
55
+
56
+ const edges: Edge[] = [
57
+ { id: 'e1-2', source: 'agent-1', target: 'agent-2' },
58
+ { id: 'e2-3', source: 'agent-2', target: 'agent-3' },
59
+ ];
60
+
61
+ expect(() => {
62
+ layoutService.applyHierarchicalLayout(nodes, edges, { direction: 'TB' });
63
+ }).toThrow('Layout service not implemented');
64
+
65
+ // Expected behavior after implementation:
66
+ // const result = layoutService.applyHierarchicalLayout(nodes, edges, { direction: 'TB' });
67
+ //
68
+ // expect(result.nodes).toHaveLength(3);
69
+ //
70
+ // // Verify vertical hierarchy: agent-1 should be above agent-2, agent-2 above agent-3
71
+ // const node1 = result.nodes.find(n => n.id === 'agent-1');
72
+ // const node2 = result.nodes.find(n => n.id === 'agent-2');
73
+ // const node3 = result.nodes.find(n => n.id === 'agent-3');
74
+ //
75
+ // expect(node1!.position.y).toBeLessThan(node2!.position.y);
76
+ // expect(node2!.position.y).toBeLessThan(node3!.position.y);
77
+ });
78
+
79
+ it('should generate hierarchical layout in horizontal direction (LR)', () => {
80
+ const nodes: Node[] = [
81
+ { id: 'agent-1', type: 'agent', position: { x: 0, y: 0 }, data: { name: 'Agent 1' } },
82
+ { id: 'agent-2', type: 'agent', position: { x: 0, y: 0 }, data: { name: 'Agent 2' } },
83
+ ];
84
+
85
+ const edges: Edge[] = [
86
+ { id: 'e1-2', source: 'agent-1', target: 'agent-2' },
87
+ ];
88
+
89
+ expect(() => {
90
+ layoutService.applyHierarchicalLayout(nodes, edges, { direction: 'LR' });
91
+ }).toThrow('Layout service not implemented');
92
+
93
+ // Expected behavior after implementation:
94
+ // const result = layoutService.applyHierarchicalLayout(nodes, edges, { direction: 'LR' });
95
+ //
96
+ // const node1 = result.nodes.find(n => n.id === 'agent-1');
97
+ // const node2 = result.nodes.find(n => n.id === 'agent-2');
98
+ //
99
+ // // Verify horizontal hierarchy: agent-1 should be left of agent-2
100
+ // expect(node1!.position.x).toBeLessThan(node2!.position.x);
101
+ });
102
+
103
+ it('should handle cyclic graphs gracefully', () => {
104
+ const nodes: Node[] = [
105
+ { id: 'agent-1', type: 'agent', position: { x: 0, y: 0 }, data: {} },
106
+ { id: 'agent-2', type: 'agent', position: { x: 0, y: 0 }, data: {} },
107
+ { id: 'agent-3', type: 'agent', position: { x: 0, y: 0 }, data: {} },
108
+ ];
109
+
110
+ const edges: Edge[] = [
111
+ { id: 'e1-2', source: 'agent-1', target: 'agent-2' },
112
+ { id: 'e2-3', source: 'agent-2', target: 'agent-3' },
113
+ { id: 'e3-1', source: 'agent-3', target: 'agent-1' }, // Cycle
114
+ ];
115
+
116
+ expect(() => {
117
+ layoutService.applyHierarchicalLayout(nodes, edges);
118
+ }).toThrow('Layout service not implemented');
119
+
120
+ // Expected behavior: Should not throw, should produce valid layout
121
+ // const result = layoutService.applyHierarchicalLayout(nodes, edges);
122
+ // expect(result.nodes).toHaveLength(3);
123
+ // result.nodes.forEach(node => {
124
+ // expect(node.position.x).toBeGreaterThanOrEqual(0);
125
+ // expect(node.position.y).toBeGreaterThanOrEqual(0);
126
+ // });
127
+ });
128
+ });
129
+
130
+ describe('Node Positioning', () => {
131
+ it('should assign x, y coordinates to all nodes', () => {
132
+ const nodes: Node[] = [
133
+ { id: 'node-1', type: 'agent', position: { x: 0, y: 0 }, data: {} },
134
+ { id: 'node-2', type: 'agent', position: { x: 0, y: 0 }, data: {} },
135
+ { id: 'node-3', type: 'agent', position: { x: 0, y: 0 }, data: {} },
136
+ ];
137
+
138
+ const edges: Edge[] = [
139
+ { id: 'e1-2', source: 'node-1', target: 'node-2' },
140
+ ];
141
+
142
+ expect(() => {
143
+ layoutService.applyHierarchicalLayout(nodes, edges);
144
+ }).toThrow('Layout service not implemented');
145
+
146
+ // Expected behavior:
147
+ // const result = layoutService.applyHierarchicalLayout(nodes, edges);
148
+ //
149
+ // result.nodes.forEach(node => {
150
+ // expect(node.position).toHaveProperty('x');
151
+ // expect(node.position).toHaveProperty('y');
152
+ // expect(typeof node.position.x).toBe('number');
153
+ // expect(typeof node.position.y).toBe('number');
154
+ // expect(Number.isFinite(node.position.x)).toBe(true);
155
+ // expect(Number.isFinite(node.position.y)).toBe(true);
156
+ // });
157
+ });
158
+
159
+ it('should prevent node overlap with proper spacing', () => {
160
+ const nodes: Node[] = Array.from({ length: 5 }, (_, i) => ({
161
+ id: `node-${i}`,
162
+ type: 'agent',
163
+ position: { x: 0, y: 0 },
164
+ data: { name: `Agent ${i}` },
165
+ }));
166
+
167
+ const edges: Edge[] = Array.from({ length: 4 }, (_, i) => ({
168
+ id: `e${i}-${i + 1}`,
169
+ source: `node-${i}`,
170
+ target: `node-${i + 1}`,
171
+ }));
172
+
173
+ expect(() => {
174
+ layoutService.applyHierarchicalLayout(nodes, edges, { nodeSpacing: 50 });
175
+ }).toThrow('Layout service not implemented');
176
+
177
+ // Expected behavior:
178
+ // const result = layoutService.applyHierarchicalLayout(nodes, edges, { nodeSpacing: 50 });
179
+ //
180
+ // // Check that no two nodes are too close
181
+ // const minDistance = 50; // Based on nodeSpacing
182
+ // for (let i = 0; i < result.nodes.length; i++) {
183
+ // for (let j = i + 1; j < result.nodes.length; j++) {
184
+ // const node1 = result.nodes[i];
185
+ // const node2 = result.nodes[j];
186
+ // const distance = Math.sqrt(
187
+ // Math.pow(node1.position.x - node2.position.x, 2) +
188
+ // Math.pow(node1.position.y - node2.position.y, 2)
189
+ // );
190
+ // expect(distance).toBeGreaterThanOrEqual(minDistance);
191
+ // }
192
+ // }
193
+ });
194
+
195
+ it('should respect custom node spacing', () => {
196
+ const nodes: Node[] = [
197
+ { id: 'node-1', type: 'agent', position: { x: 0, y: 0 }, data: {} },
198
+ { id: 'node-2', type: 'agent', position: { x: 0, y: 0 }, data: {} },
199
+ ];
200
+
201
+ const edges: Edge[] = [
202
+ { id: 'e1-2', source: 'node-1', target: 'node-2' },
203
+ ];
204
+
205
+ expect(() => {
206
+ const customSpacing = 100;
207
+ layoutService.applyHierarchicalLayout(nodes, edges, { nodeSpacing: customSpacing });
208
+ }).toThrow('Layout service not implemented');
209
+
210
+ // Expected behavior:
211
+ // const result = layoutService.applyHierarchicalLayout(nodes, edges, { nodeSpacing: 100 });
212
+ //
213
+ // const node1 = result.nodes.find(n => n.id === 'node-1')!;
214
+ // const node2 = result.nodes.find(n => n.id === 'node-2')!;
215
+ //
216
+ // // Distance should be at least the custom spacing
217
+ // const distance = Math.abs(node2.position.y - node1.position.y);
218
+ // expect(distance).toBeGreaterThanOrEqual(100);
219
+ });
220
+ });
221
+
222
+ describe('Layout Performance', () => {
223
+ it('should complete layout in <200ms for 10 nodes (REQUIREMENT)', () => {
224
+ const nodes: Node[] = Array.from({ length: 10 }, (_, i) => ({
225
+ id: `node-${i}`,
226
+ type: 'agent',
227
+ position: { x: 0, y: 0 },
228
+ data: { name: `Agent ${i}` },
229
+ }));
230
+
231
+ // Create a connected graph
232
+ const edges: Edge[] = Array.from({ length: 9 }, (_, i) => ({
233
+ id: `e${i}-${i + 1}`,
234
+ source: `node-${i}`,
235
+ target: `node-${i + 1}`,
236
+ }));
237
+
238
+ expect(() => {
239
+ const startTime = performance.now();
240
+ layoutService.applyHierarchicalLayout(nodes, edges);
241
+ const endTime = performance.now();
242
+ const duration = endTime - startTime;
243
+
244
+ // This will fail until implementation
245
+ expect(duration).toBeLessThan(200);
246
+ }).toThrow('Layout service not implemented');
247
+
248
+ // Expected behavior:
249
+ // const startTime = performance.now();
250
+ // const result = layoutService.applyHierarchicalLayout(nodes, edges);
251
+ // const endTime = performance.now();
252
+ // const duration = endTime - startTime;
253
+ //
254
+ // expect(duration).toBeLessThan(200); // REQUIREMENT: <200ms for 10 nodes
255
+ // expect(result.nodes).toHaveLength(10);
256
+ });
257
+
258
+ it('should handle large graphs efficiently (50 nodes)', () => {
259
+ const nodes: Node[] = Array.from({ length: 50 }, (_, i) => ({
260
+ id: `node-${i}`,
261
+ type: 'agent',
262
+ position: { x: 0, y: 0 },
263
+ data: { name: `Agent ${i}` },
264
+ }));
265
+
266
+ // Create edges with some complexity (not just linear)
267
+ const edges: Edge[] = [];
268
+ for (let i = 0; i < 49; i++) {
269
+ edges.push({
270
+ id: `e${i}-${i + 1}`,
271
+ source: `node-${i}`,
272
+ target: `node-${i + 1}`,
273
+ });
274
+ // Add some cross-edges for complexity
275
+ if (i % 5 === 0 && i + 2 < 50) {
276
+ edges.push({
277
+ id: `e${i}-${i + 2}`,
278
+ source: `node-${i}`,
279
+ target: `node-${i + 2}`,
280
+ });
281
+ }
282
+ }
283
+
284
+ expect(() => {
285
+ const startTime = performance.now();
286
+ layoutService.applyHierarchicalLayout(nodes, edges);
287
+ const endTime = performance.now();
288
+ const duration = endTime - startTime;
289
+
290
+ // Should still be reasonably fast (<1s)
291
+ expect(duration).toBeLessThan(1000);
292
+ }).toThrow('Layout service not implemented');
293
+ });
294
+ });
295
+
296
+ describe('Edge Routing', () => {
297
+ it('should preserve edge connections after layout', () => {
298
+ const nodes: Node[] = [
299
+ { id: 'a', type: 'agent', position: { x: 0, y: 0 }, data: {} },
300
+ { id: 'b', type: 'agent', position: { x: 0, y: 0 }, data: {} },
301
+ { id: 'c', type: 'agent', position: { x: 0, y: 0 }, data: {} },
302
+ ];
303
+
304
+ const edges: Edge[] = [
305
+ { id: 'e-a-b', source: 'a', target: 'b' },
306
+ { id: 'e-b-c', source: 'b', target: 'c' },
307
+ ];
308
+
309
+ expect(() => {
310
+ layoutService.applyHierarchicalLayout(nodes, edges);
311
+ }).toThrow('Layout service not implemented');
312
+
313
+ // Expected behavior:
314
+ // const result = layoutService.applyHierarchicalLayout(nodes, edges);
315
+ //
316
+ // // Edges should remain unchanged in structure
317
+ // expect(result.edges).toHaveLength(2);
318
+ // expect(result.edges.find(e => e.id === 'e-a-b')).toBeDefined();
319
+ // expect(result.edges.find(e => e.id === 'e-b-c')).toBeDefined();
320
+ //
321
+ // // Source and target should still match
322
+ // const edgeAB = result.edges.find(e => e.id === 'e-a-b')!;
323
+ // expect(edgeAB.source).toBe('a');
324
+ // expect(edgeAB.target).toBe('b');
325
+ });
326
+
327
+ it('should return layout dimensions (width, height)', () => {
328
+ const nodes: Node[] = [
329
+ { id: 'node-1', type: 'agent', position: { x: 0, y: 0 }, data: {} },
330
+ { id: 'node-2', type: 'agent', position: { x: 0, y: 0 }, data: {} },
331
+ ];
332
+
333
+ const edges: Edge[] = [
334
+ { id: 'e1-2', source: 'node-1', target: 'node-2' },
335
+ ];
336
+
337
+ expect(() => {
338
+ layoutService.applyHierarchicalLayout(nodes, edges);
339
+ }).toThrow('Layout service not implemented');
340
+
341
+ // Expected behavior:
342
+ // const result = layoutService.applyHierarchicalLayout(nodes, edges);
343
+ //
344
+ // expect(result).toHaveProperty('width');
345
+ // expect(result).toHaveProperty('height');
346
+ // expect(typeof result.width).toBe('number');
347
+ // expect(typeof result.height).toBe('number');
348
+ // expect(result.width).toBeGreaterThan(0);
349
+ // expect(result.height).toBeGreaterThan(0);
350
+ });
351
+
352
+ it('should handle disconnected components', () => {
353
+ const nodes: Node[] = [
354
+ { id: 'a1', type: 'agent', position: { x: 0, y: 0 }, data: {} },
355
+ { id: 'a2', type: 'agent', position: { x: 0, y: 0 }, data: {} },
356
+ { id: 'b1', type: 'agent', position: { x: 0, y: 0 }, data: {} },
357
+ { id: 'b2', type: 'agent', position: { x: 0, y: 0 }, data: {} },
358
+ ];
359
+
360
+ const edges: Edge[] = [
361
+ { id: 'e-a1-a2', source: 'a1', target: 'a2' },
362
+ { id: 'e-b1-b2', source: 'b1', target: 'b2' },
363
+ // No edges between a* and b* - two separate components
364
+ ];
365
+
366
+ expect(() => {
367
+ layoutService.applyHierarchicalLayout(nodes, edges);
368
+ }).toThrow('Layout service not implemented');
369
+
370
+ // Expected behavior:
371
+ // const result = layoutService.applyHierarchicalLayout(nodes, edges);
372
+ //
373
+ // // All nodes should still get positions
374
+ // expect(result.nodes).toHaveLength(4);
375
+ // result.nodes.forEach(node => {
376
+ // expect(Number.isFinite(node.position.x)).toBe(true);
377
+ // expect(Number.isFinite(node.position.y)).toBe(true);
378
+ // });
379
+ });
380
+ });
381
+
382
+ describe('Empty and Edge Cases', () => {
383
+ it('should handle empty node list', () => {
384
+ const nodes: Node[] = [];
385
+ const edges: Edge[] = [];
386
+
387
+ expect(() => {
388
+ layoutService.applyHierarchicalLayout(nodes, edges);
389
+ }).toThrow('Layout service not implemented');
390
+
391
+ // Expected behavior:
392
+ // const result = layoutService.applyHierarchicalLayout(nodes, edges);
393
+ // expect(result.nodes).toHaveLength(0);
394
+ // expect(result.edges).toHaveLength(0);
395
+ });
396
+
397
+ it('should handle single node with no edges', () => {
398
+ const nodes: Node[] = [
399
+ { id: 'lone-node', type: 'agent', position: { x: 0, y: 0 }, data: {} },
400
+ ];
401
+ const edges: Edge[] = [];
402
+
403
+ expect(() => {
404
+ layoutService.applyHierarchicalLayout(nodes, edges);
405
+ }).toThrow('Layout service not implemented');
406
+
407
+ // Expected behavior:
408
+ // const result = layoutService.applyHierarchicalLayout(nodes, edges);
409
+ // expect(result.nodes).toHaveLength(1);
410
+ // expect(result.nodes[0].position.x).toBeGreaterThanOrEqual(0);
411
+ // expect(result.nodes[0].position.y).toBeGreaterThanOrEqual(0);
412
+ });
413
+
414
+ it('should handle nodes with no connecting edges', () => {
415
+ const nodes: Node[] = [
416
+ { id: 'node-1', type: 'agent', position: { x: 0, y: 0 }, data: {} },
417
+ { id: 'node-2', type: 'agent', position: { x: 0, y: 0 }, data: {} },
418
+ { id: 'node-3', type: 'agent', position: { x: 0, y: 0 }, data: {} },
419
+ ];
420
+ const edges: Edge[] = [];
421
+
422
+ expect(() => {
423
+ layoutService.applyHierarchicalLayout(nodes, edges);
424
+ }).toThrow('Layout service not implemented');
425
+
426
+ // Expected behavior:
427
+ // const result = layoutService.applyHierarchicalLayout(nodes, edges);
428
+ // expect(result.nodes).toHaveLength(3);
429
+ //
430
+ // // All nodes should get valid positions
431
+ // result.nodes.forEach(node => {
432
+ // expect(Number.isFinite(node.position.x)).toBe(true);
433
+ // expect(Number.isFinite(node.position.y)).toBe(true);
434
+ // });
435
+ });
436
+ });
437
+ });
@@ -0,0 +1,146 @@
1
+ import dagre from 'dagre';
2
+ import { Node, Edge } from '@xyflow/react';
3
+
4
+ /**
5
+ * Phase 4: Graph Visualization & Dual Views - Layout Service
6
+ *
7
+ * Provides Dagre-based hierarchical layout algorithm for automatic node positioning.
8
+ * Supports both vertical (TB) and horizontal (LR) layouts with configurable spacing.
9
+ *
10
+ * REQUIREMENT: Must complete <200ms for 10 nodes
11
+ * SPECIFICATION: docs/specs/003-real-time-dashboard/PLAN.md Phase 4
12
+ */
13
+
14
+ export interface LayoutOptions {
15
+ direction?: 'TB' | 'LR' | 'BT' | 'RL';
16
+ nodeSpacing?: number;
17
+ rankSpacing?: number;
18
+ }
19
+
20
+ export interface LayoutResult {
21
+ nodes: Node[];
22
+ edges: Edge[];
23
+ width: number;
24
+ height: number;
25
+ }
26
+
27
+ // Default node dimensions
28
+ const DEFAULT_NODE_WIDTH = 200;
29
+ const DEFAULT_NODE_HEIGHT = 80;
30
+ const MESSAGE_NODE_WIDTH = 150;
31
+ const MESSAGE_NODE_HEIGHT = 60;
32
+
33
+ // Default spacing (increased by 50% for better label visibility)
34
+ const DEFAULT_NODE_SPACING = 75; // Was 50
35
+ const DEFAULT_RANK_SPACING = 150; // Was 100
36
+
37
+ /**
38
+ * Get node dimensions based on node type
39
+ */
40
+ function getNodeDimensions(node: Node): { width: number; height: number } {
41
+ if (node.type === 'message') {
42
+ return { width: MESSAGE_NODE_WIDTH, height: MESSAGE_NODE_HEIGHT };
43
+ }
44
+ return { width: DEFAULT_NODE_WIDTH, height: DEFAULT_NODE_HEIGHT };
45
+ }
46
+
47
+ /**
48
+ * Apply hierarchical layout using Dagre algorithm
49
+ *
50
+ * @param nodes - Array of nodes to layout
51
+ * @param edges - Array of edges defining connections
52
+ * @param options - Layout configuration options
53
+ * @returns Layout result with positioned nodes and graph dimensions
54
+ */
55
+ export function applyHierarchicalLayout(
56
+ nodes: Node[],
57
+ edges: Edge[],
58
+ options: LayoutOptions = {}
59
+ ): LayoutResult {
60
+ const {
61
+ direction = 'TB',
62
+ nodeSpacing = DEFAULT_NODE_SPACING,
63
+ rankSpacing = DEFAULT_RANK_SPACING,
64
+ } = options;
65
+
66
+ // Handle empty graph
67
+ if (nodes.length === 0) {
68
+ return { nodes: [], edges, width: 0, height: 0 };
69
+ }
70
+
71
+ // Create a new directed graph
72
+ const graph = new dagre.graphlib.Graph();
73
+
74
+ // Set graph layout options
75
+ graph.setGraph({
76
+ rankdir: direction,
77
+ nodesep: nodeSpacing,
78
+ ranksep: rankSpacing,
79
+ marginx: 20,
80
+ marginy: 20,
81
+ });
82
+
83
+ // Default edge configuration
84
+ graph.setDefaultEdgeLabel(() => ({}));
85
+
86
+ // Add nodes to the graph with their dimensions
87
+ nodes.forEach((node) => {
88
+ const { width, height } = getNodeDimensions(node);
89
+ graph.setNode(node.id, { width, height });
90
+ });
91
+
92
+ // Add edges to the graph
93
+ edges.forEach((edge) => {
94
+ graph.setEdge(edge.source, edge.target);
95
+ });
96
+
97
+ // Run the layout algorithm
98
+ dagre.layout(graph);
99
+
100
+ // Extract positioned nodes
101
+ const layoutedNodes = nodes.map((node) => {
102
+ const nodeWithPosition = graph.node(node.id);
103
+
104
+ // Dagre positions nodes at their center, we need top-left corner
105
+ const { width, height } = getNodeDimensions(node);
106
+
107
+ return {
108
+ ...node,
109
+ position: {
110
+ x: nodeWithPosition.x - width / 2,
111
+ y: nodeWithPosition.y - height / 2,
112
+ },
113
+ };
114
+ });
115
+
116
+ // Get graph dimensions
117
+ const graphConfig = graph.graph();
118
+ const width = (graphConfig.width || 0) + 40; // Add margin
119
+ const height = (graphConfig.height || 0) + 40; // Add margin
120
+
121
+ return {
122
+ nodes: layoutedNodes,
123
+ edges,
124
+ width,
125
+ height,
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Legacy function name for backwards compatibility
131
+ * Delegates to applyHierarchicalLayout
132
+ */
133
+ export function applyDagreLayout(
134
+ nodes: Node[],
135
+ edges: Edge[],
136
+ direction: 'TB' | 'LR' = 'TB',
137
+ nodeSpacing?: number,
138
+ rankSpacing?: number
139
+ ): Node[] {
140
+ const result = applyHierarchicalLayout(nodes, edges, {
141
+ direction,
142
+ nodeSpacing,
143
+ rankSpacing
144
+ });
145
+ return result.nodes;
146
+ }