flowent 0.0.7 → 0.0.11

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.
Files changed (387) hide show
  1. package/README.md +0 -3
  2. package/backend/README.md +0 -3
  3. package/backend/pyproject.toml +2 -8
  4. package/backend/src/flowent/__init__.py +6 -2
  5. package/backend/src/flowent/__pycache__/__init__.cpython-313.pyc +0 -0
  6. package/backend/src/flowent/__pycache__/agent.cpython-313.pyc +0 -0
  7. package/backend/src/flowent/__pycache__/context.cpython-313.pyc +0 -0
  8. package/backend/src/flowent/__pycache__/llm.cpython-313.pyc +0 -0
  9. package/backend/src/flowent/__pycache__/logging.cpython-313.pyc +0 -0
  10. package/backend/src/flowent/__pycache__/main.cpython-313.pyc +0 -0
  11. package/backend/src/flowent/__pycache__/patch.cpython-313.pyc +0 -0
  12. package/backend/src/flowent/__pycache__/paths.cpython-313.pyc +0 -0
  13. package/backend/src/flowent/__pycache__/sandbox.cpython-313.pyc +0 -0
  14. package/backend/src/flowent/__pycache__/storage.cpython-313.pyc +0 -0
  15. package/backend/src/flowent/__pycache__/tools.cpython-313.pyc +0 -0
  16. package/backend/src/flowent/agent.py +213 -3173
  17. package/backend/src/flowent/cli.py +19 -24
  18. package/backend/src/flowent/context.py +127 -0
  19. package/backend/src/flowent/llm.py +256 -0
  20. package/backend/src/flowent/logging.py +170 -129
  21. package/backend/src/flowent/main.py +321 -70
  22. package/backend/src/flowent/patch.py +182 -0
  23. package/backend/src/flowent/paths.py +11 -0
  24. package/backend/src/flowent/sandbox.py +214 -40
  25. package/backend/src/flowent/static/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
  26. package/backend/src/flowent/static/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
  27. package/backend/src/flowent/static/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
  28. package/backend/src/flowent/static/assets/index-C76K95ty.js +81 -0
  29. package/backend/src/flowent/static/assets/index-iUMNKvlU.css +2 -0
  30. package/backend/src/flowent/static/flowent.png +0 -0
  31. package/backend/src/flowent/static/index.html +5 -25
  32. package/backend/src/flowent/storage.py +302 -0
  33. package/backend/src/flowent/tools.py +376 -0
  34. package/backend/tests/__pycache__/test_agent_tools.cpython-313-pytest-9.0.3.pyc +0 -0
  35. package/backend/tests/__pycache__/test_health.cpython-313-pytest-9.0.3.pyc +0 -0
  36. package/backend/tests/__pycache__/test_llm_providers.cpython-313-pytest-9.0.3.pyc +0 -0
  37. package/backend/tests/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
  38. package/backend/tests/__pycache__/test_persistence.cpython-313-pytest-9.0.3.pyc +0 -0
  39. package/backend/tests/__pycache__/test_workspace_chat.cpython-313-pytest-9.0.3.pyc +0 -0
  40. package/backend/tests/test_agent_tools.py +477 -0
  41. package/backend/tests/test_health.py +12 -0
  42. package/backend/tests/test_llm_providers.py +113 -0
  43. package/backend/tests/test_logging.py +182 -0
  44. package/backend/tests/test_persistence.py +125 -0
  45. package/backend/tests/test_workspace_chat.py +578 -0
  46. package/backend/uv.lock +803 -99
  47. package/dist/frontend/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
  48. package/dist/frontend/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
  49. package/dist/frontend/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
  50. package/dist/frontend/assets/index-C76K95ty.js +81 -0
  51. package/dist/frontend/assets/index-iUMNKvlU.css +2 -0
  52. package/dist/frontend/flowent.png +0 -0
  53. package/dist/frontend/index.html +5 -25
  54. package/package.json +1 -2
  55. package/backend/src/flowent/__pycache__/_version.cpython-313.pyc +0 -0
  56. package/backend/src/flowent/__pycache__/access.cpython-313.pyc +0 -0
  57. package/backend/src/flowent/__pycache__/assistant_commands.cpython-313.pyc +0 -0
  58. package/backend/src/flowent/__pycache__/cli.cpython-313.pyc +0 -0
  59. package/backend/src/flowent/__pycache__/config.cpython-313.pyc +0 -0
  60. package/backend/src/flowent/__pycache__/events.cpython-313.pyc +0 -0
  61. package/backend/src/flowent/__pycache__/graph_runtime.cpython-313.pyc +0 -0
  62. package/backend/src/flowent/__pycache__/graph_service.cpython-313.pyc +0 -0
  63. package/backend/src/flowent/__pycache__/image_assets.cpython-313.pyc +0 -0
  64. package/backend/src/flowent/__pycache__/model_metadata.cpython-313.pyc +0 -0
  65. package/backend/src/flowent/__pycache__/network.cpython-313.pyc +0 -0
  66. package/backend/src/flowent/__pycache__/observability_service.cpython-313.pyc +0 -0
  67. package/backend/src/flowent/__pycache__/registry.cpython-313.pyc +0 -0
  68. package/backend/src/flowent/__pycache__/role_management.cpython-313.pyc +0 -0
  69. package/backend/src/flowent/__pycache__/runtime.cpython-313.pyc +0 -0
  70. package/backend/src/flowent/__pycache__/security.cpython-313.pyc +0 -0
  71. package/backend/src/flowent/__pycache__/settings.cpython-313.pyc +0 -0
  72. package/backend/src/flowent/__pycache__/settings_management.cpython-313.pyc +0 -0
  73. package/backend/src/flowent/__pycache__/state_db.cpython-313.pyc +0 -0
  74. package/backend/src/flowent/__pycache__/workspace_store.cpython-313.pyc +0 -0
  75. package/backend/src/flowent/access.py +0 -247
  76. package/backend/src/flowent/assistant_commands.py +0 -115
  77. package/backend/src/flowent/channels/__init__.py +0 -3
  78. package/backend/src/flowent/channels/__pycache__/__init__.cpython-313.pyc +0 -0
  79. package/backend/src/flowent/channels/__pycache__/telegram.cpython-313.pyc +0 -0
  80. package/backend/src/flowent/channels/telegram.py +0 -615
  81. package/backend/src/flowent/config.py +0 -14
  82. package/backend/src/flowent/dev.py +0 -3
  83. package/backend/src/flowent/events.py +0 -157
  84. package/backend/src/flowent/graph_runtime.py +0 -60
  85. package/backend/src/flowent/graph_service.py +0 -2401
  86. package/backend/src/flowent/image_assets.py +0 -356
  87. package/backend/src/flowent/model_metadata.py +0 -102
  88. package/backend/src/flowent/models/__init__.py +0 -125
  89. package/backend/src/flowent/models/__pycache__/__init__.cpython-313.pyc +0 -0
  90. package/backend/src/flowent/models/__pycache__/agent.cpython-313.pyc +0 -0
  91. package/backend/src/flowent/models/__pycache__/base.cpython-313.pyc +0 -0
  92. package/backend/src/flowent/models/__pycache__/blueprint.cpython-313.pyc +0 -0
  93. package/backend/src/flowent/models/__pycache__/content.cpython-313.pyc +0 -0
  94. package/backend/src/flowent/models/__pycache__/delta.cpython-313.pyc +0 -0
  95. package/backend/src/flowent/models/__pycache__/event.cpython-313.pyc +0 -0
  96. package/backend/src/flowent/models/__pycache__/graph.cpython-313.pyc +0 -0
  97. package/backend/src/flowent/models/__pycache__/history.cpython-313.pyc +0 -0
  98. package/backend/src/flowent/models/__pycache__/llm.cpython-313.pyc +0 -0
  99. package/backend/src/flowent/models/__pycache__/message.cpython-313.pyc +0 -0
  100. package/backend/src/flowent/models/__pycache__/tab.cpython-313.pyc +0 -0
  101. package/backend/src/flowent/models/__pycache__/todo.cpython-313.pyc +0 -0
  102. package/backend/src/flowent/models/agent.py +0 -34
  103. package/backend/src/flowent/models/base.py +0 -24
  104. package/backend/src/flowent/models/blueprint.py +0 -176
  105. package/backend/src/flowent/models/content.py +0 -164
  106. package/backend/src/flowent/models/delta.py +0 -44
  107. package/backend/src/flowent/models/event.py +0 -51
  108. package/backend/src/flowent/models/graph.py +0 -472
  109. package/backend/src/flowent/models/history.py +0 -272
  110. package/backend/src/flowent/models/llm.py +0 -62
  111. package/backend/src/flowent/models/message.py +0 -33
  112. package/backend/src/flowent/models/tab.py +0 -85
  113. package/backend/src/flowent/models/todo.py +0 -10
  114. package/backend/src/flowent/network.py +0 -146
  115. package/backend/src/flowent/observability_service.py +0 -218
  116. package/backend/src/flowent/prompts/__init__.py +0 -67
  117. package/backend/src/flowent/prompts/__pycache__/__init__.cpython-313.pyc +0 -0
  118. package/backend/src/flowent/prompts/__pycache__/common.cpython-313.pyc +0 -0
  119. package/backend/src/flowent/prompts/__pycache__/steward.cpython-313.pyc +0 -0
  120. package/backend/src/flowent/prompts/common.py +0 -250
  121. package/backend/src/flowent/prompts/steward.py +0 -64
  122. package/backend/src/flowent/providers/__init__.py +0 -23
  123. package/backend/src/flowent/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  124. package/backend/src/flowent/providers/__pycache__/anthropic.cpython-313.pyc +0 -0
  125. package/backend/src/flowent/providers/__pycache__/base_url.cpython-313.pyc +0 -0
  126. package/backend/src/flowent/providers/__pycache__/configuration.cpython-313.pyc +0 -0
  127. package/backend/src/flowent/providers/__pycache__/content.cpython-313.pyc +0 -0
  128. package/backend/src/flowent/providers/__pycache__/errors.cpython-313.pyc +0 -0
  129. package/backend/src/flowent/providers/__pycache__/gateway.cpython-313.pyc +0 -0
  130. package/backend/src/flowent/providers/__pycache__/headers.cpython-313.pyc +0 -0
  131. package/backend/src/flowent/providers/__pycache__/management.cpython-313.pyc +0 -0
  132. package/backend/src/flowent/providers/__pycache__/openai.cpython-313.pyc +0 -0
  133. package/backend/src/flowent/providers/__pycache__/openai_responses.cpython-313.pyc +0 -0
  134. package/backend/src/flowent/providers/__pycache__/registry.cpython-313.pyc +0 -0
  135. package/backend/src/flowent/providers/__pycache__/sse.cpython-313.pyc +0 -0
  136. package/backend/src/flowent/providers/__pycache__/thinking.cpython-313.pyc +0 -0
  137. package/backend/src/flowent/providers/anthropic.py +0 -468
  138. package/backend/src/flowent/providers/base_url.py +0 -60
  139. package/backend/src/flowent/providers/configuration.py +0 -189
  140. package/backend/src/flowent/providers/content.py +0 -122
  141. package/backend/src/flowent/providers/errors.py +0 -223
  142. package/backend/src/flowent/providers/gateway.py +0 -169
  143. package/backend/src/flowent/providers/gemini.py +0 -447
  144. package/backend/src/flowent/providers/headers.py +0 -20
  145. package/backend/src/flowent/providers/management.py +0 -96
  146. package/backend/src/flowent/providers/ollama.py +0 -293
  147. package/backend/src/flowent/providers/openai.py +0 -422
  148. package/backend/src/flowent/providers/openai_responses.py +0 -655
  149. package/backend/src/flowent/providers/registry.py +0 -144
  150. package/backend/src/flowent/providers/sse.py +0 -31
  151. package/backend/src/flowent/providers/thinking.py +0 -79
  152. package/backend/src/flowent/registry.py +0 -73
  153. package/backend/src/flowent/role_management.py +0 -270
  154. package/backend/src/flowent/routes/__init__.py +0 -26
  155. package/backend/src/flowent/routes/__pycache__/__init__.cpython-313.pyc +0 -0
  156. package/backend/src/flowent/routes/__pycache__/access.cpython-313.pyc +0 -0
  157. package/backend/src/flowent/routes/__pycache__/assistant.cpython-313.pyc +0 -0
  158. package/backend/src/flowent/routes/__pycache__/image_assets.cpython-313.pyc +0 -0
  159. package/backend/src/flowent/routes/__pycache__/meta.cpython-313.pyc +0 -0
  160. package/backend/src/flowent/routes/__pycache__/nodes.cpython-313.pyc +0 -0
  161. package/backend/src/flowent/routes/__pycache__/prompts.cpython-313.pyc +0 -0
  162. package/backend/src/flowent/routes/__pycache__/providers_route.cpython-313.pyc +0 -0
  163. package/backend/src/flowent/routes/__pycache__/roles.cpython-313.pyc +0 -0
  164. package/backend/src/flowent/routes/__pycache__/settings.cpython-313.pyc +0 -0
  165. package/backend/src/flowent/routes/__pycache__/tabs.cpython-313.pyc +0 -0
  166. package/backend/src/flowent/routes/__pycache__/ws.cpython-313.pyc +0 -0
  167. package/backend/src/flowent/routes/access.py +0 -48
  168. package/backend/src/flowent/routes/assistant.py +0 -158
  169. package/backend/src/flowent/routes/image_assets.py +0 -33
  170. package/backend/src/flowent/routes/meta.py +0 -28
  171. package/backend/src/flowent/routes/nodes.py +0 -423
  172. package/backend/src/flowent/routes/prompts.py +0 -46
  173. package/backend/src/flowent/routes/providers_route.py +0 -365
  174. package/backend/src/flowent/routes/roles.py +0 -207
  175. package/backend/src/flowent/routes/settings.py +0 -379
  176. package/backend/src/flowent/routes/tabs.py +0 -298
  177. package/backend/src/flowent/routes/ws.py +0 -33
  178. package/backend/src/flowent/runtime.py +0 -160
  179. package/backend/src/flowent/security.py +0 -37
  180. package/backend/src/flowent/settings.py +0 -2112
  181. package/backend/src/flowent/settings_management.py +0 -394
  182. package/backend/src/flowent/state_db.py +0 -108
  183. package/backend/src/flowent/static/assets/AssistantPage-BW7XAd9I.js +0 -1
  184. package/backend/src/flowent/static/assets/ChannelsPage-tCJHgt6m.js +0 -1
  185. package/backend/src/flowent/static/assets/PageScaffold-f6g2l7XN.js +0 -1
  186. package/backend/src/flowent/static/assets/PromptsPage-C3Sxn2D7.js +0 -1
  187. package/backend/src/flowent/static/assets/ProvidersPage-BfmdXmNt.js +0 -3
  188. package/backend/src/flowent/static/assets/RolesPage-DET8wO4r.js +0 -1
  189. package/backend/src/flowent/static/assets/SettingsPage-D-g3deMm.js +0 -3
  190. package/backend/src/flowent/static/assets/ToolsPage-CDmtE2g4.js +0 -1
  191. package/backend/src/flowent/static/assets/WorkspacePage-AZsJ0sD0.js +0 -3
  192. package/backend/src/flowent/static/assets/WorkspacePanels-CteCjolX.js +0 -1
  193. package/backend/src/flowent/static/assets/alert-dialog-Duorp_S-.js +0 -1
  194. package/backend/src/flowent/static/assets/dialog-C3ixjGjN.js +0 -1
  195. package/backend/src/flowent/static/assets/elk-worker.min-C9JGDOE-.js +0 -6312
  196. package/backend/src/flowent/static/assets/graph-vendor-CHpVij2M.css +0 -1
  197. package/backend/src/flowent/static/assets/graph-vendor-DRq_-6fV.js +0 -7
  198. package/backend/src/flowent/static/assets/index--o_0fv0N.css +0 -1
  199. package/backend/src/flowent/static/assets/index-C9HuekJm.js +0 -10
  200. package/backend/src/flowent/static/assets/layout.worker-jMHqAFbP.js +0 -24
  201. package/backend/src/flowent/static/assets/markdown-vendor-C9RtvaJh.js +0 -29
  202. package/backend/src/flowent/static/assets/modelParams-DmnF2hwR.js +0 -1
  203. package/backend/src/flowent/static/assets/providerTypes-DT3Ahwl_.js +0 -1
  204. package/backend/src/flowent/static/assets/react-vendor-mEs_JJxa.js +0 -9
  205. package/backend/src/flowent/static/assets/roles-CuRT_chR.js +0 -1
  206. package/backend/src/flowent/static/assets/rolldown-runtime-BYbx6iT9.js +0 -1
  207. package/backend/src/flowent/static/assets/select-DCfeNu-F.js +0 -1
  208. package/backend/src/flowent/static/assets/surface-pWwG5ogx.js +0 -1
  209. package/backend/src/flowent/static/assets/ui-vendor-C5pJa8N7.js +0 -51
  210. package/backend/src/flowent/static/assets/useAppRoute-FgSHBKhV.js +0 -1
  211. package/backend/src/flowent/static/favicon.svg +0 -4
  212. package/backend/src/flowent/tools/__init__.py +0 -176
  213. package/backend/src/flowent/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  214. package/backend/src/flowent/tools/__pycache__/connect.cpython-313.pyc +0 -0
  215. package/backend/src/flowent/tools/__pycache__/contacts.cpython-313.pyc +0 -0
  216. package/backend/src/flowent/tools/__pycache__/create_agent.cpython-313.pyc +0 -0
  217. package/backend/src/flowent/tools/__pycache__/create_tab.cpython-313.pyc +0 -0
  218. package/backend/src/flowent/tools/__pycache__/delete_tab.cpython-313.pyc +0 -0
  219. package/backend/src/flowent/tools/__pycache__/edit.cpython-313.pyc +0 -0
  220. package/backend/src/flowent/tools/__pycache__/exec.cpython-313.pyc +0 -0
  221. package/backend/src/flowent/tools/__pycache__/fetch.cpython-313.pyc +0 -0
  222. package/backend/src/flowent/tools/__pycache__/idle.cpython-313.pyc +0 -0
  223. package/backend/src/flowent/tools/__pycache__/list_roles.cpython-313.pyc +0 -0
  224. package/backend/src/flowent/tools/__pycache__/list_tabs.cpython-313.pyc +0 -0
  225. package/backend/src/flowent/tools/__pycache__/list_tools.cpython-313.pyc +0 -0
  226. package/backend/src/flowent/tools/__pycache__/manage_prompts.cpython-313.pyc +0 -0
  227. package/backend/src/flowent/tools/__pycache__/manage_providers.cpython-313.pyc +0 -0
  228. package/backend/src/flowent/tools/__pycache__/manage_roles.cpython-313.pyc +0 -0
  229. package/backend/src/flowent/tools/__pycache__/manage_settings.cpython-313.pyc +0 -0
  230. package/backend/src/flowent/tools/__pycache__/read.cpython-313.pyc +0 -0
  231. package/backend/src/flowent/tools/__pycache__/send.cpython-313.pyc +0 -0
  232. package/backend/src/flowent/tools/__pycache__/set_permissions.cpython-313.pyc +0 -0
  233. package/backend/src/flowent/tools/__pycache__/sleep.cpython-313.pyc +0 -0
  234. package/backend/src/flowent/tools/__pycache__/todo.cpython-313.pyc +0 -0
  235. package/backend/src/flowent/tools/connect.py +0 -100
  236. package/backend/src/flowent/tools/contacts.py +0 -22
  237. package/backend/src/flowent/tools/create_agent.py +0 -191
  238. package/backend/src/flowent/tools/create_tab.py +0 -61
  239. package/backend/src/flowent/tools/delete_tab.py +0 -39
  240. package/backend/src/flowent/tools/edit.py +0 -142
  241. package/backend/src/flowent/tools/exec.py +0 -118
  242. package/backend/src/flowent/tools/fetch.py +0 -85
  243. package/backend/src/flowent/tools/idle.py +0 -27
  244. package/backend/src/flowent/tools/list_roles.py +0 -68
  245. package/backend/src/flowent/tools/list_tabs.py +0 -100
  246. package/backend/src/flowent/tools/list_tools.py +0 -28
  247. package/backend/src/flowent/tools/manage_prompts.py +0 -102
  248. package/backend/src/flowent/tools/manage_providers.py +0 -220
  249. package/backend/src/flowent/tools/manage_roles.py +0 -275
  250. package/backend/src/flowent/tools/manage_settings.py +0 -326
  251. package/backend/src/flowent/tools/read.py +0 -152
  252. package/backend/src/flowent/tools/send.py +0 -68
  253. package/backend/src/flowent/tools/set_permissions.py +0 -99
  254. package/backend/src/flowent/tools/sleep.py +0 -41
  255. package/backend/src/flowent/tools/todo.py +0 -51
  256. package/backend/src/flowent/workspace_store.py +0 -479
  257. package/backend/tests/__init__.py +0 -0
  258. package/backend/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  259. package/backend/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  260. package/backend/tests/conftest.py +0 -6
  261. package/backend/tests/integration/api/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  262. package/backend/tests/integration/api/__pycache__/test_access_api.cpython-313-pytest-9.0.3.pyc +0 -0
  263. package/backend/tests/integration/api/__pycache__/test_assistant_api.cpython-313-pytest-9.0.3.pyc +0 -0
  264. package/backend/tests/integration/api/__pycache__/test_frontend_mounting.cpython-313-pytest-9.0.3.pyc +0 -0
  265. package/backend/tests/integration/api/__pycache__/test_meta_api.cpython-313-pytest-9.0.3.pyc +0 -0
  266. package/backend/tests/integration/api/__pycache__/test_nodes_api.cpython-313-pytest-9.0.3.pyc +0 -0
  267. package/backend/tests/integration/api/__pycache__/test_prompts_api.cpython-313-pytest-9.0.3.pyc +0 -0
  268. package/backend/tests/integration/api/__pycache__/test_roles_api.cpython-313-pytest-9.0.3.pyc +0 -0
  269. package/backend/tests/integration/api/__pycache__/test_tabs_api.cpython-313-pytest-9.0.3.pyc +0 -0
  270. package/backend/tests/integration/api/conftest.py +0 -29
  271. package/backend/tests/integration/api/test_access_api.py +0 -182
  272. package/backend/tests/integration/api/test_assistant_api.py +0 -422
  273. package/backend/tests/integration/api/test_frontend_mounting.py +0 -61
  274. package/backend/tests/integration/api/test_meta_api.py +0 -32
  275. package/backend/tests/integration/api/test_nodes_api.py +0 -787
  276. package/backend/tests/integration/api/test_prompts_api.py +0 -47
  277. package/backend/tests/integration/api/test_roles_api.py +0 -228
  278. package/backend/tests/integration/api/test_tabs_api.py +0 -688
  279. package/backend/tests/unit/__pycache__/test_access.cpython-313-pytest-9.0.3.pyc +0 -0
  280. package/backend/tests/unit/__pycache__/test_cli.cpython-313-pytest-9.0.3.pyc +0 -0
  281. package/backend/tests/unit/__pycache__/test_graph_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  282. package/backend/tests/unit/__pycache__/test_network.cpython-313-pytest-9.0.3.pyc +0 -0
  283. package/backend/tests/unit/__pycache__/test_state_sqlite_storage.cpython-313-pytest-9.0.3.pyc +0 -0
  284. package/backend/tests/unit/__pycache__/test_workspace_store.cpython-313-pytest-9.0.3.pyc +0 -0
  285. package/backend/tests/unit/agent/__pycache__/test_agent_public_api.cpython-313-pytest-9.0.3.pyc +0 -0
  286. package/backend/tests/unit/agent/__pycache__/test_agent_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  287. package/backend/tests/unit/agent/test_agent_public_api.py +0 -822
  288. package/backend/tests/unit/agent/test_agent_runtime.py +0 -3088
  289. package/backend/tests/unit/channels/__pycache__/test_telegram_channel.cpython-313-pytest-9.0.3.pyc +0 -0
  290. package/backend/tests/unit/channels/test_telegram_channel.py +0 -552
  291. package/backend/tests/unit/logging/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
  292. package/backend/tests/unit/logging/test_logging.py +0 -132
  293. package/backend/tests/unit/prompts/__pycache__/test_prompts.cpython-313-pytest-9.0.3.pyc +0 -0
  294. package/backend/tests/unit/prompts/test_prompts.py +0 -570
  295. package/backend/tests/unit/providers/__pycache__/test_anthropic_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  296. package/backend/tests/unit/providers/__pycache__/test_errors.cpython-313-pytest-9.0.3.pyc +0 -0
  297. package/backend/tests/unit/providers/__pycache__/test_extract_delta_parts.cpython-313-pytest-9.0.3.pyc +0 -0
  298. package/backend/tests/unit/providers/__pycache__/test_openai_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  299. package/backend/tests/unit/providers/__pycache__/test_openai_responses.cpython-313-pytest-9.0.3.pyc +0 -0
  300. package/backend/tests/unit/providers/__pycache__/test_provider_gateway.cpython-313-pytest-9.0.3.pyc +0 -0
  301. package/backend/tests/unit/providers/__pycache__/test_think_tag_parser.cpython-313-pytest-9.0.3.pyc +0 -0
  302. package/backend/tests/unit/providers/test_anthropic_provider.py +0 -185
  303. package/backend/tests/unit/providers/test_errors.py +0 -68
  304. package/backend/tests/unit/providers/test_extract_delta_parts.py +0 -22
  305. package/backend/tests/unit/providers/test_openai_provider.py +0 -139
  306. package/backend/tests/unit/providers/test_openai_responses.py +0 -402
  307. package/backend/tests/unit/providers/test_provider_gateway.py +0 -359
  308. package/backend/tests/unit/providers/test_think_tag_parser.py +0 -36
  309. package/backend/tests/unit/routes/__pycache__/test_prompts_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  310. package/backend/tests/unit/routes/__pycache__/test_providers_route.cpython-313-pytest-9.0.3.pyc +0 -0
  311. package/backend/tests/unit/routes/__pycache__/test_roles_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  312. package/backend/tests/unit/routes/__pycache__/test_settings_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  313. package/backend/tests/unit/routes/test_prompts_routes.py +0 -82
  314. package/backend/tests/unit/routes/test_providers_route.py +0 -370
  315. package/backend/tests/unit/routes/test_roles_routes.py +0 -539
  316. package/backend/tests/unit/routes/test_settings_routes.py +0 -1123
  317. package/backend/tests/unit/runtime/__pycache__/test_bootstrap_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  318. package/backend/tests/unit/runtime/test_bootstrap_runtime.py +0 -1002
  319. package/backend/tests/unit/sandbox/__pycache__/test_sandbox_tools.cpython-313-pytest-9.0.3.pyc +0 -0
  320. package/backend/tests/unit/sandbox/test_sandbox_tools.py +0 -78
  321. package/backend/tests/unit/security/__pycache__/test_security.cpython-313-pytest-9.0.3.pyc +0 -0
  322. package/backend/tests/unit/security/test_security.py +0 -124
  323. package/backend/tests/unit/settings/__pycache__/test_settings_roles.cpython-313-pytest-9.0.3.pyc +0 -0
  324. package/backend/tests/unit/settings/test_settings_roles.py +0 -703
  325. package/backend/tests/unit/test_access.py +0 -45
  326. package/backend/tests/unit/test_cli.py +0 -102
  327. package/backend/tests/unit/test_graph_runtime.py +0 -72
  328. package/backend/tests/unit/test_network.py +0 -51
  329. package/backend/tests/unit/test_state_sqlite_storage.py +0 -87
  330. package/backend/tests/unit/test_workspace_store.py +0 -228
  331. package/backend/tests/unit/tools/__pycache__/test_connect_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  332. package/backend/tests/unit/tools/__pycache__/test_create_agent_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  333. package/backend/tests/unit/tools/__pycache__/test_delete_tab_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  334. package/backend/tests/unit/tools/__pycache__/test_edit_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  335. package/backend/tests/unit/tools/__pycache__/test_exec_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  336. package/backend/tests/unit/tools/__pycache__/test_fetch_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  337. package/backend/tests/unit/tools/__pycache__/test_manage_prompts_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  338. package/backend/tests/unit/tools/__pycache__/test_manage_providers_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  339. package/backend/tests/unit/tools/__pycache__/test_manage_roles_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  340. package/backend/tests/unit/tools/__pycache__/test_manage_settings_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  341. package/backend/tests/unit/tools/__pycache__/test_read_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  342. package/backend/tests/unit/tools/__pycache__/test_set_permissions_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  343. package/backend/tests/unit/tools/__pycache__/test_todo_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  344. package/backend/tests/unit/tools/__pycache__/test_tool_registry.cpython-313-pytest-9.0.3.pyc +0 -0
  345. package/backend/tests/unit/tools/test_connect_tool.py +0 -228
  346. package/backend/tests/unit/tools/test_create_agent_tool.py +0 -404
  347. package/backend/tests/unit/tools/test_delete_tab_tool.py +0 -116
  348. package/backend/tests/unit/tools/test_edit_tool.py +0 -115
  349. package/backend/tests/unit/tools/test_exec_tool.py +0 -81
  350. package/backend/tests/unit/tools/test_fetch_tool.py +0 -65
  351. package/backend/tests/unit/tools/test_manage_prompts_tool.py +0 -92
  352. package/backend/tests/unit/tools/test_manage_providers_tool.py +0 -460
  353. package/backend/tests/unit/tools/test_manage_roles_tool.py +0 -411
  354. package/backend/tests/unit/tools/test_manage_settings_tool.py +0 -611
  355. package/backend/tests/unit/tools/test_read_tool.py +0 -33
  356. package/backend/tests/unit/tools/test_set_permissions_tool.py +0 -595
  357. package/backend/tests/unit/tools/test_todo_tool.py +0 -37
  358. package/backend/tests/unit/tools/test_tool_registry.py +0 -199
  359. package/dist/frontend/assets/AssistantPage-BW7XAd9I.js +0 -1
  360. package/dist/frontend/assets/ChannelsPage-tCJHgt6m.js +0 -1
  361. package/dist/frontend/assets/PageScaffold-f6g2l7XN.js +0 -1
  362. package/dist/frontend/assets/PromptsPage-C3Sxn2D7.js +0 -1
  363. package/dist/frontend/assets/ProvidersPage-BfmdXmNt.js +0 -3
  364. package/dist/frontend/assets/RolesPage-DET8wO4r.js +0 -1
  365. package/dist/frontend/assets/SettingsPage-D-g3deMm.js +0 -3
  366. package/dist/frontend/assets/ToolsPage-CDmtE2g4.js +0 -1
  367. package/dist/frontend/assets/WorkspacePage-AZsJ0sD0.js +0 -3
  368. package/dist/frontend/assets/WorkspacePanels-CteCjolX.js +0 -1
  369. package/dist/frontend/assets/alert-dialog-Duorp_S-.js +0 -1
  370. package/dist/frontend/assets/dialog-C3ixjGjN.js +0 -1
  371. package/dist/frontend/assets/elk-worker.min-C9JGDOE-.js +0 -6312
  372. package/dist/frontend/assets/graph-vendor-CHpVij2M.css +0 -1
  373. package/dist/frontend/assets/graph-vendor-DRq_-6fV.js +0 -7
  374. package/dist/frontend/assets/index--o_0fv0N.css +0 -1
  375. package/dist/frontend/assets/index-C9HuekJm.js +0 -10
  376. package/dist/frontend/assets/layout.worker-jMHqAFbP.js +0 -24
  377. package/dist/frontend/assets/markdown-vendor-C9RtvaJh.js +0 -29
  378. package/dist/frontend/assets/modelParams-DmnF2hwR.js +0 -1
  379. package/dist/frontend/assets/providerTypes-DT3Ahwl_.js +0 -1
  380. package/dist/frontend/assets/react-vendor-mEs_JJxa.js +0 -9
  381. package/dist/frontend/assets/roles-CuRT_chR.js +0 -1
  382. package/dist/frontend/assets/rolldown-runtime-BYbx6iT9.js +0 -1
  383. package/dist/frontend/assets/select-DCfeNu-F.js +0 -1
  384. package/dist/frontend/assets/surface-pWwG5ogx.js +0 -1
  385. package/dist/frontend/assets/ui-vendor-C5pJa8N7.js +0 -51
  386. package/dist/frontend/assets/useAppRoute-FgSHBKhV.js +0 -1
  387. package/dist/frontend/favicon.svg +0 -4
@@ -1,379 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import time
4
- from copy import deepcopy
5
- from dataclasses import dataclass
6
-
7
- from fastapi import APIRouter, HTTPException
8
- from loguru import logger
9
- from pydantic import BaseModel, ConfigDict
10
-
11
- from flowent.access import initialize_live_access_signature, set_access_code
12
- from flowent.events import event_bus
13
- from flowent.settings import (
14
- TelegramApprovedChat,
15
- TelegramSettings,
16
- get_settings,
17
- save_settings,
18
- serialize_provider,
19
- serialize_role,
20
- serialize_settings,
21
- serialize_telegram_settings,
22
- )
23
- from flowent.settings_management import (
24
- MISSING,
25
- apply_resolved_settings_update,
26
- resolve_settings_update,
27
- )
28
-
29
- router = APIRouter()
30
-
31
-
32
- @router.get("/api/settings/bootstrap")
33
- async def get_settings_bootstrap() -> dict[str, object]:
34
- from flowent._version import __version__
35
-
36
- settings = get_settings()
37
- return {
38
- "settings": serialize_settings(settings),
39
- "providers": [serialize_provider(provider) for provider in settings.providers],
40
- "roles": [serialize_role(role) for role in settings.roles],
41
- "version": __version__,
42
- }
43
-
44
-
45
- @router.get("/api/settings")
46
- async def get_settings_api() -> dict[str, object]:
47
- settings = get_settings()
48
- return serialize_settings(settings)
49
-
50
-
51
- class UpdateSettingsRequest(BaseModel):
52
- model_config = ConfigDict(extra="forbid")
53
- access: dict[str, object] | None = None
54
- assistant: dict[str, object] | None = None
55
- event_log: dict[str, object] | None = None
56
- leader: dict[str, object] | None = None
57
- model: dict[str, object] | None = None
58
- working_dir: str | None = None
59
-
60
-
61
- class UpdateTelegramSettingsRequest(BaseModel):
62
- model_config = ConfigDict(extra="forbid")
63
- bot_token: str | None = None
64
-
65
-
66
- @dataclass(frozen=True, slots=True)
67
- class AssistantSettingsPatch:
68
- role_name: str | None = None
69
- allow_network: object = MISSING
70
- write_dirs: object = MISSING
71
-
72
-
73
- @dataclass(frozen=True, slots=True)
74
- class ModelSettingsPatch:
75
- active_provider_id: str | None = None
76
- active_model: str | None = None
77
- context_window_tokens: object = MISSING
78
- input_image: object = MISSING
79
- output_image: object = MISSING
80
- structured_output: object = MISSING
81
- timeout_ms: object = MISSING
82
- retry_policy: object = MISSING
83
- max_retries: object = MISSING
84
- retry_initial_delay_seconds: object = MISSING
85
- retry_max_delay_seconds: object = MISSING
86
- retry_backoff_cap_retries: object = MISSING
87
- auto_compact_token_limit: object = MISSING
88
- params: object = MISSING
89
-
90
-
91
- def _extract_access_code_rotation(access: dict[str, object] | None) -> str | None:
92
- if access is None:
93
- return None
94
-
95
- raw_new_code = access.get("new_code", "")
96
- raw_confirm_code = access.get("confirm_code", "")
97
- if raw_new_code is not None and not isinstance(raw_new_code, str):
98
- raise HTTPException(status_code=400, detail="access.new_code must be a string")
99
- if raw_confirm_code is not None and not isinstance(raw_confirm_code, str):
100
- raise HTTPException(
101
- status_code=400,
102
- detail="access.confirm_code must be a string",
103
- )
104
-
105
- new_code = raw_new_code if isinstance(raw_new_code, str) else ""
106
- confirm_code = raw_confirm_code if isinstance(raw_confirm_code, str) else ""
107
- if not new_code and not confirm_code:
108
- return None
109
- if not new_code.strip():
110
- raise HTTPException(
111
- status_code=400,
112
- detail="access.new_code must not be empty",
113
- )
114
- if confirm_code != new_code:
115
- raise HTTPException(
116
- status_code=400,
117
- detail="access.confirm_code must match access.new_code",
118
- )
119
- return new_code
120
-
121
-
122
- def _extract_assistant_settings_patch(
123
- assistant: dict[str, object] | None,
124
- ) -> AssistantSettingsPatch:
125
- if assistant is None:
126
- return AssistantSettingsPatch()
127
-
128
- unknown_fields = sorted(
129
- set(assistant) - {"role_name", "allow_network", "write_dirs"}
130
- )
131
- if unknown_fields:
132
- raise HTTPException(
133
- status_code=400,
134
- detail="Unknown assistant fields: " + ", ".join(unknown_fields),
135
- )
136
-
137
- raw_role_name = assistant.get("role_name")
138
- return AssistantSettingsPatch(
139
- role_name=(
140
- raw_role_name
141
- if isinstance(raw_role_name, str) and raw_role_name.strip()
142
- else None
143
- ),
144
- allow_network=(
145
- assistant.get("allow_network") if "allow_network" in assistant else MISSING
146
- ),
147
- write_dirs=assistant.get("write_dirs")
148
- if "write_dirs" in assistant
149
- else MISSING,
150
- )
151
-
152
-
153
- def _extract_role_name(payload: dict[str, object] | None) -> str | None:
154
- if payload is None:
155
- return None
156
- raw_role_name = payload.get("role_name")
157
- if isinstance(raw_role_name, str) and raw_role_name.strip():
158
- return raw_role_name
159
- return None
160
-
161
-
162
- def _extract_timestamp_format(payload: dict[str, object] | None) -> str | None:
163
- if payload is None:
164
- return None
165
- raw_timestamp_format = payload.get("timestamp_format")
166
- return raw_timestamp_format if isinstance(raw_timestamp_format, str) else None
167
-
168
-
169
- def _extract_model_settings_patch(
170
- model: dict[str, object] | None,
171
- ) -> ModelSettingsPatch:
172
- if model is None:
173
- return ModelSettingsPatch()
174
-
175
- raw_active_provider_id = model.get("active_provider_id")
176
- raw_active_model = model.get("active_model")
177
- return ModelSettingsPatch(
178
- active_provider_id=(
179
- raw_active_provider_id if isinstance(raw_active_provider_id, str) else None
180
- ),
181
- active_model=raw_active_model if isinstance(raw_active_model, str) else None,
182
- context_window_tokens=(
183
- model.get("context_window_tokens")
184
- if "context_window_tokens" in model
185
- else MISSING
186
- ),
187
- input_image=model.get("input_image") if "input_image" in model else MISSING,
188
- output_image=model.get("output_image") if "output_image" in model else MISSING,
189
- structured_output=(
190
- model.get("structured_output") if "structured_output" in model else MISSING
191
- ),
192
- timeout_ms=model.get("timeout_ms") if "timeout_ms" in model else MISSING,
193
- retry_policy=model.get("retry_policy") if "retry_policy" in model else MISSING,
194
- max_retries=model.get("max_retries") if "max_retries" in model else MISSING,
195
- retry_initial_delay_seconds=(
196
- model.get("retry_initial_delay_seconds")
197
- if "retry_initial_delay_seconds" in model
198
- else MISSING
199
- ),
200
- retry_max_delay_seconds=(
201
- model.get("retry_max_delay_seconds")
202
- if "retry_max_delay_seconds" in model
203
- else MISSING
204
- ),
205
- retry_backoff_cap_retries=(
206
- model.get("retry_backoff_cap_retries")
207
- if "retry_backoff_cap_retries" in model
208
- else MISSING
209
- ),
210
- auto_compact_token_limit=(
211
- model.get("auto_compact_token_limit")
212
- if "auto_compact_token_limit" in model
213
- else MISSING
214
- ),
215
- params=model.get("params") if "params" in model else MISSING,
216
- )
217
-
218
-
219
- @router.post("/api/settings")
220
- async def update_settings(req: UpdateSettingsRequest) -> dict[str, object]:
221
- from flowent.graph_service import sync_assistant_role, sync_tab_leaders
222
- from flowent.providers.gateway import gateway
223
-
224
- source_settings = get_settings()
225
- current = deepcopy(source_settings)
226
- next_access_code = _extract_access_code_rotation(req.access)
227
- reauth_required = False
228
- assistant_patch = _extract_assistant_settings_patch(req.assistant)
229
- leader_role_name = _extract_role_name(req.leader)
230
- timestamp_format = _extract_timestamp_format(req.event_log)
231
- model_patch = _extract_model_settings_patch(req.model)
232
-
233
- try:
234
- resolved = resolve_settings_update(
235
- current,
236
- working_dir=req.working_dir,
237
- assistant_role_name=assistant_patch.role_name,
238
- assistant_allow_network=assistant_patch.allow_network,
239
- assistant_write_dirs=assistant_patch.write_dirs,
240
- leader_role_name=leader_role_name,
241
- active_provider_id=model_patch.active_provider_id,
242
- active_model=model_patch.active_model,
243
- context_window_tokens=model_patch.context_window_tokens,
244
- input_image=model_patch.input_image,
245
- output_image=model_patch.output_image,
246
- structured_output=model_patch.structured_output,
247
- max_retries=model_patch.max_retries,
248
- retry_policy=model_patch.retry_policy,
249
- timeout_ms=model_patch.timeout_ms,
250
- retry_initial_delay_seconds=model_patch.retry_initial_delay_seconds,
251
- retry_max_delay_seconds=model_patch.retry_max_delay_seconds,
252
- retry_backoff_cap_retries=model_patch.retry_backoff_cap_retries,
253
- auto_compact_token_limit=model_patch.auto_compact_token_limit,
254
- model_params=model_patch.params,
255
- timestamp_format=timestamp_format,
256
- )
257
- except ValueError as exc:
258
- raise HTTPException(status_code=400, detail=str(exc)) from exc
259
-
260
- apply_resolved_settings_update(current, resolved)
261
-
262
- if next_access_code is not None:
263
- set_access_code(current, next_access_code)
264
- reauth_required = True
265
-
266
- save_settings(current)
267
- source_settings.__dict__.clear()
268
- source_settings.__dict__.update(deepcopy(current).__dict__)
269
- initialize_live_access_signature()
270
- try:
271
- sync_assistant_role(reason="assistant settings updated")
272
- sync_tab_leaders(reason="leader settings updated")
273
- gateway.invalidate_cache()
274
- except Exception:
275
- logger.exception("Settings saved but runtime synchronization failed")
276
- if reauth_required:
277
- event_bus.close_all_connections(code=4001, reason="Access code rotated")
278
- logger.info("Settings updated")
279
- return {
280
- "status": "saved",
281
- "settings": serialize_settings(current),
282
- "reauth_required": reauth_required,
283
- }
284
-
285
-
286
- @router.get("/api/settings/telegram")
287
- async def get_telegram_settings() -> dict[str, object]:
288
- settings = get_settings()
289
- return serialize_telegram_settings(settings.telegram)
290
-
291
-
292
- @router.patch("/api/settings/telegram")
293
- async def update_telegram_settings(
294
- req: UpdateTelegramSettingsRequest,
295
- ) -> dict[str, object]:
296
- from flowent.runtime import restart_telegram_channel
297
-
298
- settings = get_settings()
299
- previous_token = settings.telegram.bot_token
300
-
301
- next_token = previous_token
302
- if req.bot_token is not None:
303
- next_token = req.bot_token.strip()
304
-
305
- settings.telegram = TelegramSettings(
306
- bot_token=next_token,
307
- pending_chats=list(settings.telegram.pending_chats),
308
- approved_chats=list(settings.telegram.approved_chats),
309
- )
310
- save_settings(settings)
311
-
312
- if next_token != previous_token:
313
- restart_telegram_channel()
314
-
315
- logger.info("Telegram settings updated")
316
- return {
317
- "status": "saved",
318
- "telegram": serialize_telegram_settings(settings.telegram),
319
- }
320
-
321
-
322
- @router.post("/api/settings/telegram/approve/{chat_id}")
323
- async def approve_telegram_chat(chat_id: int) -> dict[str, object]:
324
- settings = get_settings()
325
- pending_chat = next(
326
- (chat for chat in settings.telegram.pending_chats if chat.chat_id == chat_id),
327
- None,
328
- )
329
- if pending_chat is None:
330
- raise HTTPException(status_code=404, detail="Pending Telegram chat not found")
331
-
332
- settings.telegram.pending_chats = [
333
- chat for chat in settings.telegram.pending_chats if chat.chat_id != chat_id
334
- ]
335
- if not any(chat.chat_id == chat_id for chat in settings.telegram.approved_chats):
336
- settings.telegram.approved_chats.append(
337
- TelegramApprovedChat(
338
- chat_id=pending_chat.chat_id,
339
- username=pending_chat.username,
340
- display_name=pending_chat.display_name,
341
- approved_at=time.time(),
342
- )
343
- )
344
- save_settings(settings)
345
- logger.info("Telegram chat approved: {}", chat_id)
346
- return {
347
- "status": "approved",
348
- "telegram": serialize_telegram_settings(settings.telegram),
349
- }
350
-
351
-
352
- @router.delete("/api/settings/telegram/pending/{chat_id}")
353
- async def delete_pending_telegram_chat(chat_id: int) -> dict[str, object]:
354
- settings = get_settings()
355
- settings.telegram.pending_chats = [
356
- chat for chat in settings.telegram.pending_chats if chat.chat_id != chat_id
357
- ]
358
- save_settings(settings)
359
- logger.info("Pending Telegram chat removed: {}", chat_id)
360
- return {
361
- "status": "deleted",
362
- "telegram": serialize_telegram_settings(settings.telegram),
363
- }
364
-
365
-
366
- @router.delete("/api/settings/telegram/chat/{chat_id}")
367
- async def delete_telegram_chat(chat_id: int) -> dict[str, object]:
368
- settings = get_settings()
369
- settings.telegram.approved_chats = [
370
- existing_chat
371
- for existing_chat in settings.telegram.approved_chats
372
- if existing_chat.chat_id != chat_id
373
- ]
374
- save_settings(settings)
375
- logger.info("Telegram chat removed: {}", chat_id)
376
- return {
377
- "status": "deleted",
378
- "telegram": serialize_telegram_settings(settings.telegram),
379
- }
@@ -1,298 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from fastapi import APIRouter, HTTPException
4
- from pydantic import BaseModel, ConfigDict
5
-
6
- from flowent.graph_service import (
7
- activate_tab,
8
- create_agent_node,
9
- create_edge,
10
- create_graph_node,
11
- create_tab,
12
- deactivate_tab,
13
- delete_agent_node,
14
- delete_edge,
15
- delete_tab,
16
- list_node_connection_ids,
17
- list_tab_edges,
18
- list_workflow_nodes,
19
- serialize_tab_summary,
20
- update_tab_definition,
21
- )
22
- from flowent.models import AgentState, WorkflowNodeKind
23
- from flowent.registry import registry
24
- from flowent.workspace_store import workspace_store
25
-
26
- router = APIRouter()
27
-
28
-
29
- class CreateTabRequest(BaseModel):
30
- model_config = ConfigDict(extra="forbid")
31
- title: str
32
- allow_network: bool = False
33
- write_dirs: list[str] = []
34
-
35
-
36
- class CreateTabNodeRequest(BaseModel):
37
- model_config = ConfigDict(extra="forbid")
38
- node_type: str = WorkflowNodeKind.AGENT.value
39
- role_name: str | None = None
40
- name: str | None = None
41
- config: dict[str, object] = {}
42
- tools: list[str] = []
43
-
44
-
45
- class CreateTabEdgeRequest(BaseModel):
46
- model_config = ConfigDict(extra="forbid")
47
- from_node_id: str
48
- from_port_key: str = "out"
49
- to_node_id: str
50
- to_port_key: str = "in"
51
- kind: str | None = None
52
-
53
-
54
- class UpdateTabDefinitionRequest(BaseModel):
55
- model_config = ConfigDict(extra="forbid")
56
- definition: dict[str, object]
57
-
58
-
59
- def _serialize_workflow_node(
60
- *,
61
- tab_id: str,
62
- node_id: str,
63
- ) -> dict[str, object]:
64
- tab = workspace_store.get_tab(tab_id)
65
- if tab is None:
66
- raise HTTPException(status_code=404, detail="Workflow not found")
67
- definition = tab.definition.get_node(node_id)
68
- if definition is None:
69
- raise HTTPException(status_code=404, detail="Node not found")
70
- record = workspace_store.get_node_record(node_id)
71
- live_node = registry.get(node_id)
72
- config = dict(definition.config)
73
- role_name = config.get("role_name")
74
- name = config.get("name")
75
- position = tab.definition.view.positions.get(node_id)
76
- todos = (
77
- [todo.serialize() for todo in live_node.get_todos_snapshot()]
78
- if live_node is not None
79
- else [todo.serialize() for todo in (record.todos if record is not None else [])]
80
- )
81
- state = (
82
- live_node.state.value
83
- if live_node is not None
84
- else (record.state.value if record is not None else AgentState.IDLE.value)
85
- )
86
- return {
87
- "id": definition.id,
88
- "node_type": definition.type.value,
89
- "workflow_id": tab_id,
90
- "role_name": role_name if isinstance(role_name, str) else None,
91
- "is_leader": False,
92
- "state": state,
93
- "connections": list_node_connection_ids(tab_id=tab_id, node_id=definition.id)
94
- if definition.type == WorkflowNodeKind.AGENT
95
- else [],
96
- "name": name if isinstance(name, str) else None,
97
- "todos": todos if definition.type == WorkflowNodeKind.AGENT else [],
98
- "position": position.serialize() if position is not None else None,
99
- "config": config,
100
- "inputs": [port.serialize() for port in definition.inputs],
101
- "outputs": [port.serialize() for port in definition.outputs],
102
- }
103
-
104
-
105
- @router.get("/api/workflows")
106
- async def list_workflows() -> dict[str, object]:
107
- tabs = workspace_store.list_tabs()
108
- return {"workflows": [serialize_tab_summary(tab) for tab in tabs]}
109
-
110
-
111
- @router.post("/api/workflows")
112
- async def create_workflow_route(req: CreateTabRequest) -> dict[str, object]:
113
- title = req.title.strip()
114
- if not title:
115
- raise HTTPException(status_code=400, detail="title must not be empty")
116
- try:
117
- tab = create_tab(
118
- title=title,
119
- allow_network=req.allow_network,
120
- write_dirs=req.write_dirs,
121
- )
122
- except ValueError as exc:
123
- raise HTTPException(status_code=400, detail=str(exc)) from exc
124
- return serialize_tab_summary(tab)
125
-
126
-
127
- @router.post("/api/workflows/{tab_id}/activate")
128
- async def activate_workflow_route(tab_id: str) -> dict[str, object]:
129
- updated, errors, error = activate_tab(tab_id=tab_id, actor_id=tab_id)
130
- if errors:
131
- raise HTTPException(status_code=400, detail={"errors": errors})
132
- if error is not None or updated is None:
133
- raise HTTPException(
134
- status_code=404 if error and error.endswith("not found") else 400,
135
- detail=error or "Failed to activate workflow",
136
- )
137
- return serialize_tab_summary(updated)
138
-
139
-
140
- @router.post("/api/workflows/{tab_id}/deactivate")
141
- async def deactivate_workflow_route(tab_id: str) -> dict[str, object]:
142
- updated, error = deactivate_tab(tab_id=tab_id, actor_id=tab_id)
143
- if error is not None or updated is None:
144
- raise HTTPException(
145
- status_code=404 if error and error.endswith("not found") else 400,
146
- detail=error or "Failed to deactivate workflow",
147
- )
148
- return serialize_tab_summary(updated)
149
-
150
-
151
- @router.get("/api/workflows/{tab_id}")
152
- async def get_workflow(tab_id: str) -> dict[str, object]:
153
- tab = workspace_store.get_tab(tab_id)
154
- if tab is None:
155
- raise HTTPException(status_code=404, detail="Workflow not found")
156
- nodes = [
157
- _serialize_workflow_node(tab_id=tab_id, node_id=node.id)
158
- for node in list_workflow_nodes(tab_id)
159
- ]
160
- edges = [edge.serialize() for edge in list_tab_edges(tab_id)]
161
- return {
162
- "workflow": serialize_tab_summary(tab),
163
- "nodes": nodes,
164
- "edges": edges,
165
- }
166
-
167
-
168
- @router.put("/api/workflows/{tab_id}/definition")
169
- async def update_workflow_definition_route(
170
- tab_id: str,
171
- req: UpdateTabDefinitionRequest,
172
- ) -> dict[str, object]:
173
- updated, error = update_tab_definition(
174
- tab_id=tab_id,
175
- definition_payload=req.definition,
176
- actor_id=tab_id,
177
- )
178
- if error is not None or updated is None:
179
- raise HTTPException(
180
- status_code=400 if error and not error.endswith("not found") else 404,
181
- detail=error or "Failed to update workflow definition",
182
- )
183
- return serialize_tab_summary(updated)
184
-
185
-
186
- @router.delete("/api/workflows/{tab_id}")
187
- async def delete_workflow_route(tab_id: str) -> dict[str, object]:
188
- deleted, error = delete_tab(tab_id=tab_id)
189
- if error is not None or deleted is None:
190
- status_code = 404 if error and error.endswith("not found") else 400
191
- raise HTTPException(
192
- status_code=status_code,
193
- detail=error or "Failed to delete workflow",
194
- )
195
- return deleted
196
-
197
-
198
- @router.post("/api/workflows/{tab_id}/nodes")
199
- async def create_workflow_node(
200
- tab_id: str,
201
- req: CreateTabNodeRequest,
202
- ) -> dict[str, object]:
203
- try:
204
- node_type = WorkflowNodeKind(req.node_type.strip())
205
- except ValueError as exc:
206
- raise HTTPException(status_code=400, detail="Invalid node_type") from exc
207
-
208
- if node_type == WorkflowNodeKind.AGENT:
209
- if not isinstance(req.role_name, str) or not req.role_name.strip():
210
- raise HTTPException(status_code=400, detail="role_name is required")
211
- record, error = create_agent_node(
212
- role_name=req.role_name,
213
- tab_id=tab_id,
214
- name=req.name,
215
- tools=req.tools,
216
- )
217
- if error is not None or record is None:
218
- raise HTTPException(
219
- status_code=400, detail=error or "Failed to create node"
220
- )
221
- return _serialize_workflow_node(tab_id=tab_id, node_id=record.id)
222
-
223
- node, error = create_graph_node(
224
- tab_id=tab_id,
225
- node_type=node_type,
226
- config={
227
- **req.config,
228
- **(
229
- {"name": req.name}
230
- if isinstance(req.name, str) and req.name.strip()
231
- else {}
232
- ),
233
- },
234
- actor_id=tab_id,
235
- )
236
- if error is not None or node is None:
237
- raise HTTPException(status_code=400, detail=error or "Failed to create node")
238
- return _serialize_workflow_node(tab_id=tab_id, node_id=node.id)
239
-
240
-
241
- @router.post("/api/workflows/{tab_id}/edges")
242
- async def create_workflow_edge(
243
- tab_id: str,
244
- req: CreateTabEdgeRequest,
245
- ) -> dict[str, object]:
246
- tab = workspace_store.get_tab(tab_id)
247
- if tab is None:
248
- raise HTTPException(status_code=404, detail="Workflow not found")
249
- edge, error = create_edge(
250
- tab_id=tab_id,
251
- from_node_id=req.from_node_id,
252
- from_port_key=req.from_port_key,
253
- to_node_id=req.to_node_id,
254
- to_port_key=req.to_port_key,
255
- kind=req.kind or "",
256
- )
257
- if error is not None or edge is None:
258
- raise HTTPException(status_code=400, detail=error or "Failed to create edge")
259
- return edge.serialize()
260
-
261
-
262
- @router.delete("/api/workflows/{tab_id}/nodes/{node_id}")
263
- async def delete_workflow_node(tab_id: str, node_id: str) -> dict[str, object]:
264
- deleted, error = delete_agent_node(
265
- tab_id=tab_id,
266
- node_id=node_id,
267
- )
268
- if error is not None or deleted is None:
269
- status_code = 404 if error and error.endswith("not found") else 400
270
- raise HTTPException(
271
- status_code=status_code, detail=error or "Failed to delete node"
272
- )
273
- return deleted
274
-
275
-
276
- @router.delete("/api/workflows/{tab_id}/edges")
277
- async def delete_workflow_edge(
278
- tab_id: str,
279
- edge_id: str | None = None,
280
- from_node_id: str | None = None,
281
- to_node_id: str | None = None,
282
- from_port_key: str | None = None,
283
- to_port_key: str | None = None,
284
- ) -> dict[str, object]:
285
- deleted, error = delete_edge(
286
- tab_id=tab_id,
287
- edge_id=edge_id,
288
- from_node_id=from_node_id,
289
- to_node_id=to_node_id,
290
- from_port_key=from_port_key,
291
- to_port_key=to_port_key,
292
- )
293
- if error is not None or deleted is None:
294
- status_code = 404 if error and error.endswith("not found") else 400
295
- raise HTTPException(
296
- status_code=status_code, detail=error or "Failed to delete edge"
297
- )
298
- return deleted
@@ -1,33 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from fastapi import APIRouter
4
- from starlette.websockets import WebSocket, WebSocketDisconnect
5
-
6
- from flowent.access import authorize_websocket
7
- from flowent.events import event_bus
8
-
9
- router = APIRouter()
10
-
11
-
12
- @router.websocket("/ws/events")
13
- async def ws_events(ws: WebSocket) -> None:
14
- if not await authorize_websocket(ws):
15
- return
16
- await event_bus.connect_display(ws)
17
- try:
18
- while True:
19
- await ws.receive_text()
20
- except WebSocketDisconnect:
21
- event_bus.disconnect_display(ws)
22
-
23
-
24
- @router.websocket("/ws/updates")
25
- async def ws_updates(ws: WebSocket) -> None:
26
- if not await authorize_websocket(ws):
27
- return
28
- await event_bus.connect_updates(ws)
29
- try:
30
- while True:
31
- await ws.receive_text()
32
- except WebSocketDisconnect:
33
- event_bus.disconnect_updates(ws)