flowent 0.0.0 → 0.0.4

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 (494) hide show
  1. package/README.md +70 -10
  2. package/assets/flowent-banner.png +0 -0
  3. package/backend/.python-version +1 -0
  4. package/backend/pyproject.toml +57 -0
  5. package/backend/src/flowent/__init__.py +3 -0
  6. package/backend/src/flowent/__pycache__/__init__.cpython-313.pyc +0 -0
  7. package/backend/src/flowent/__pycache__/_version.cpython-313.pyc +0 -0
  8. package/backend/src/flowent/__pycache__/access.cpython-313.pyc +0 -0
  9. package/backend/src/flowent/__pycache__/agent.cpython-313.pyc +0 -0
  10. package/backend/src/flowent/__pycache__/assistant_commands.cpython-313.pyc +0 -0
  11. package/backend/src/flowent/__pycache__/cli.cpython-313.pyc +0 -0
  12. package/backend/src/flowent/__pycache__/config.cpython-313.pyc +0 -0
  13. package/backend/src/flowent/__pycache__/events.cpython-313.pyc +0 -0
  14. package/backend/src/flowent/__pycache__/graph_runtime.cpython-313.pyc +0 -0
  15. package/backend/src/flowent/__pycache__/graph_service.cpython-313.pyc +0 -0
  16. package/backend/src/flowent/__pycache__/image_assets.cpython-313.pyc +0 -0
  17. package/backend/src/flowent/__pycache__/logging.cpython-313.pyc +0 -0
  18. package/backend/src/flowent/__pycache__/main.cpython-313.pyc +0 -0
  19. package/backend/src/flowent/__pycache__/mcp_service.cpython-313.pyc +0 -0
  20. package/backend/src/flowent/__pycache__/model_metadata.cpython-313.pyc +0 -0
  21. package/backend/src/flowent/__pycache__/network.cpython-313.pyc +0 -0
  22. package/backend/src/flowent/__pycache__/registry.cpython-313.pyc +0 -0
  23. package/backend/src/flowent/__pycache__/role_management.cpython-313.pyc +0 -0
  24. package/backend/src/flowent/__pycache__/runtime.cpython-313.pyc +0 -0
  25. package/backend/src/flowent/__pycache__/sandbox.cpython-313.pyc +0 -0
  26. package/backend/src/flowent/__pycache__/security.cpython-313.pyc +0 -0
  27. package/backend/src/flowent/__pycache__/settings.cpython-313.pyc +0 -0
  28. package/backend/src/flowent/__pycache__/settings_management.cpython-313.pyc +0 -0
  29. package/backend/src/flowent/__pycache__/state_db.cpython-313.pyc +0 -0
  30. package/backend/src/flowent/__pycache__/stats_service.cpython-313.pyc +0 -0
  31. package/backend/src/flowent/__pycache__/workspace_store.cpython-313.pyc +0 -0
  32. package/backend/src/flowent/_version.py +7 -0
  33. package/backend/src/flowent/access.py +247 -0
  34. package/backend/src/flowent/agent.py +2808 -0
  35. package/backend/src/flowent/assistant_commands.py +106 -0
  36. package/backend/src/flowent/channels/__init__.py +3 -0
  37. package/backend/src/flowent/channels/__pycache__/__init__.cpython-313.pyc +0 -0
  38. package/backend/src/flowent/channels/__pycache__/telegram.cpython-313.pyc +0 -0
  39. package/backend/src/flowent/channels/telegram.py +615 -0
  40. package/backend/src/flowent/cli.py +85 -0
  41. package/backend/src/flowent/config.py +14 -0
  42. package/backend/src/flowent/dev.py +3 -0
  43. package/backend/src/flowent/events.py +157 -0
  44. package/backend/src/flowent/graph_runtime.py +60 -0
  45. package/backend/src/flowent/graph_service.py +1346 -0
  46. package/backend/src/flowent/image_assets.py +356 -0
  47. package/backend/src/flowent/logging.py +155 -0
  48. package/backend/src/flowent/main.py +124 -0
  49. package/backend/src/flowent/mcp_service.py +1904 -0
  50. package/backend/src/flowent/model_metadata.py +98 -0
  51. package/backend/src/flowent/models/__init__.py +121 -0
  52. package/backend/src/flowent/models/__pycache__/__init__.cpython-313.pyc +0 -0
  53. package/backend/src/flowent/models/__pycache__/agent.cpython-313.pyc +0 -0
  54. package/backend/src/flowent/models/__pycache__/base.cpython-313.pyc +0 -0
  55. package/backend/src/flowent/models/__pycache__/blueprint.cpython-313.pyc +0 -0
  56. package/backend/src/flowent/models/__pycache__/content.cpython-313.pyc +0 -0
  57. package/backend/src/flowent/models/__pycache__/delta.cpython-313.pyc +0 -0
  58. package/backend/src/flowent/models/__pycache__/event.cpython-313.pyc +0 -0
  59. package/backend/src/flowent/models/__pycache__/graph.cpython-313.pyc +0 -0
  60. package/backend/src/flowent/models/__pycache__/history.cpython-313.pyc +0 -0
  61. package/backend/src/flowent/models/__pycache__/llm.cpython-313.pyc +0 -0
  62. package/backend/src/flowent/models/__pycache__/message.cpython-313.pyc +0 -0
  63. package/backend/src/flowent/models/__pycache__/tab.cpython-313.pyc +0 -0
  64. package/backend/src/flowent/models/__pycache__/todo.cpython-313.pyc +0 -0
  65. package/backend/src/flowent/models/agent.py +33 -0
  66. package/backend/src/flowent/models/base.py +24 -0
  67. package/backend/src/flowent/models/blueprint.py +176 -0
  68. package/backend/src/flowent/models/content.py +164 -0
  69. package/backend/src/flowent/models/delta.py +44 -0
  70. package/backend/src/flowent/models/event.py +51 -0
  71. package/backend/src/flowent/models/graph.py +437 -0
  72. package/backend/src/flowent/models/history.py +214 -0
  73. package/backend/src/flowent/models/llm.py +61 -0
  74. package/backend/src/flowent/models/message.py +27 -0
  75. package/backend/src/flowent/models/tab.py +48 -0
  76. package/backend/src/flowent/models/todo.py +10 -0
  77. package/backend/src/flowent/network.py +146 -0
  78. package/backend/src/flowent/prompts/__init__.py +67 -0
  79. package/backend/src/flowent/prompts/__pycache__/__init__.cpython-313.pyc +0 -0
  80. package/backend/src/flowent/prompts/__pycache__/common.cpython-313.pyc +0 -0
  81. package/backend/src/flowent/prompts/__pycache__/steward.cpython-313.pyc +0 -0
  82. package/backend/src/flowent/prompts/common.py +250 -0
  83. package/backend/src/flowent/prompts/steward.py +64 -0
  84. package/backend/src/flowent/providers/__init__.py +23 -0
  85. package/backend/src/flowent/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  86. package/backend/src/flowent/providers/__pycache__/anthropic.cpython-313.pyc +0 -0
  87. package/backend/src/flowent/providers/__pycache__/base_url.cpython-313.pyc +0 -0
  88. package/backend/src/flowent/providers/__pycache__/configuration.cpython-313.pyc +0 -0
  89. package/backend/src/flowent/providers/__pycache__/content.cpython-313.pyc +0 -0
  90. package/backend/src/flowent/providers/__pycache__/errors.cpython-313.pyc +0 -0
  91. package/backend/src/flowent/providers/__pycache__/gateway.cpython-313.pyc +0 -0
  92. package/backend/src/flowent/providers/__pycache__/headers.cpython-313.pyc +0 -0
  93. package/backend/src/flowent/providers/__pycache__/management.cpython-313.pyc +0 -0
  94. package/backend/src/flowent/providers/__pycache__/openai.cpython-313.pyc +0 -0
  95. package/backend/src/flowent/providers/__pycache__/openai_responses.cpython-313.pyc +0 -0
  96. package/backend/src/flowent/providers/__pycache__/registry.cpython-313.pyc +0 -0
  97. package/backend/src/flowent/providers/__pycache__/sse.cpython-313.pyc +0 -0
  98. package/backend/src/flowent/providers/__pycache__/thinking.cpython-313.pyc +0 -0
  99. package/backend/src/flowent/providers/anthropic.py +468 -0
  100. package/backend/src/flowent/providers/base_url.py +60 -0
  101. package/backend/src/flowent/providers/configuration.py +182 -0
  102. package/backend/src/flowent/providers/content.py +122 -0
  103. package/backend/src/flowent/providers/errors.py +223 -0
  104. package/backend/src/flowent/providers/gateway.py +169 -0
  105. package/backend/src/flowent/providers/gemini.py +447 -0
  106. package/backend/src/flowent/providers/headers.py +20 -0
  107. package/backend/src/flowent/providers/management.py +96 -0
  108. package/backend/src/flowent/providers/ollama.py +293 -0
  109. package/backend/src/flowent/providers/openai.py +422 -0
  110. package/backend/src/flowent/providers/openai_responses.py +655 -0
  111. package/backend/src/flowent/providers/registry.py +144 -0
  112. package/backend/src/flowent/providers/sse.py +31 -0
  113. package/backend/src/flowent/providers/thinking.py +79 -0
  114. package/backend/src/flowent/registry.py +73 -0
  115. package/backend/src/flowent/role_management.py +255 -0
  116. package/backend/src/flowent/routes/__init__.py +30 -0
  117. package/backend/src/flowent/routes/__pycache__/__init__.cpython-313.pyc +0 -0
  118. package/backend/src/flowent/routes/__pycache__/access.cpython-313.pyc +0 -0
  119. package/backend/src/flowent/routes/__pycache__/assistant.cpython-313.pyc +0 -0
  120. package/backend/src/flowent/routes/__pycache__/image_assets.cpython-313.pyc +0 -0
  121. package/backend/src/flowent/routes/__pycache__/mcp.cpython-313.pyc +0 -0
  122. package/backend/src/flowent/routes/__pycache__/meta.cpython-313.pyc +0 -0
  123. package/backend/src/flowent/routes/__pycache__/nodes.cpython-313.pyc +0 -0
  124. package/backend/src/flowent/routes/__pycache__/prompts.cpython-313.pyc +0 -0
  125. package/backend/src/flowent/routes/__pycache__/providers_route.cpython-313.pyc +0 -0
  126. package/backend/src/flowent/routes/__pycache__/roles.cpython-313.pyc +0 -0
  127. package/backend/src/flowent/routes/__pycache__/settings.cpython-313.pyc +0 -0
  128. package/backend/src/flowent/routes/__pycache__/stats.cpython-313.pyc +0 -0
  129. package/backend/src/flowent/routes/__pycache__/tabs.cpython-313.pyc +0 -0
  130. package/backend/src/flowent/routes/__pycache__/ws.cpython-313.pyc +0 -0
  131. package/backend/src/flowent/routes/access.py +48 -0
  132. package/backend/src/flowent/routes/assistant.py +155 -0
  133. package/backend/src/flowent/routes/image_assets.py +33 -0
  134. package/backend/src/flowent/routes/mcp.py +125 -0
  135. package/backend/src/flowent/routes/meta.py +28 -0
  136. package/backend/src/flowent/routes/nodes.py +365 -0
  137. package/backend/src/flowent/routes/prompts.py +46 -0
  138. package/backend/src/flowent/routes/providers_route.py +364 -0
  139. package/backend/src/flowent/routes/roles.py +207 -0
  140. package/backend/src/flowent/routes/settings.py +324 -0
  141. package/backend/src/flowent/routes/stats.py +229 -0
  142. package/backend/src/flowent/routes/tabs.py +292 -0
  143. package/backend/src/flowent/routes/ws.py +33 -0
  144. package/backend/src/flowent/runtime.py +188 -0
  145. package/backend/src/flowent/sandbox.py +45 -0
  146. package/backend/src/flowent/security.py +42 -0
  147. package/backend/src/flowent/settings.py +2467 -0
  148. package/backend/src/flowent/settings_management.py +286 -0
  149. package/backend/src/flowent/state_db.py +120 -0
  150. package/backend/src/flowent/static/assets/AssistantPage-B3Xc08AS.js +1 -0
  151. package/backend/src/flowent/static/assets/ChannelsPage-ByLd28xk.js +1 -0
  152. package/backend/src/flowent/static/assets/HomePage-C0hAx9_l.js +3 -0
  153. package/backend/src/flowent/static/assets/McpPage-DkrYLvBv.js +7 -0
  154. package/backend/src/flowent/static/assets/PageScaffold-D4jO9ooX.js +1 -0
  155. package/backend/src/flowent/static/assets/PromptsPage-DWA7rRJd.js +1 -0
  156. package/backend/src/flowent/static/assets/ProvidersPage-PUWT8seJ.js +3 -0
  157. package/backend/src/flowent/static/assets/RolesPage-CqcclGRw.js +1 -0
  158. package/backend/src/flowent/static/assets/SettingsPage-8tS2cJgX.js +3 -0
  159. package/backend/src/flowent/static/assets/StatsPage-BX9khYzu.js +1 -0
  160. package/backend/src/flowent/static/assets/ToolsPage-9Tl9FdeD.js +1 -0
  161. package/backend/src/flowent/static/assets/WorkspaceCommandDialog-CCXxjDL8.js +1 -0
  162. package/backend/src/flowent/static/assets/WorkspacePanels-aMdJ7ZH7.js +1 -0
  163. package/backend/src/flowent/static/assets/alert-dialog-kFYVQ7oX.js +1 -0
  164. package/backend/src/flowent/static/assets/badge-74-3jsCg.js +1 -0
  165. package/backend/src/flowent/static/assets/constants-XUzFf6i1.js +1 -0
  166. package/backend/src/flowent/static/assets/datetime-m6_O_Ci9.js +1 -0
  167. package/backend/src/flowent/static/assets/dialog-BeGSweF6.js +1 -0
  168. package/backend/src/flowent/static/assets/elk-worker.min-C9JGDOE-.js +6312 -0
  169. package/backend/src/flowent/static/assets/graph-vendor-CHpVij2M.css +1 -0
  170. package/backend/src/flowent/static/assets/graph-vendor-DRq_-6fV.js +7 -0
  171. package/backend/src/flowent/static/assets/index-BHC1Vhy8.css +1 -0
  172. package/backend/src/flowent/static/assets/index-CL1ALZ3r.js +10 -0
  173. package/backend/src/flowent/static/assets/layout.worker-jMHqAFbP.js +24 -0
  174. package/backend/src/flowent/static/assets/markdown-vendor-DVdy_w12.js +29 -0
  175. package/backend/src/flowent/static/assets/modelParams-CaHd0903.js +1 -0
  176. package/backend/src/flowent/static/assets/react-vendor-mEs_JJxa.js +9 -0
  177. package/backend/src/flowent/static/assets/roles-2OLDeTc5.js +1 -0
  178. package/backend/src/flowent/static/assets/rolldown-runtime-BYbx6iT9.js +1 -0
  179. package/backend/src/flowent/static/assets/select-DL_LPeDj.js +1 -0
  180. package/backend/src/flowent/static/assets/shared-CMxbpLeQ.js +1 -0
  181. package/backend/src/flowent/static/assets/triState-DEr3NkXV.js +1 -0
  182. package/backend/src/flowent/static/assets/ui-vendor-Dg9NNnWX.js +51 -0
  183. package/backend/src/flowent/static/index.html +36 -0
  184. package/backend/src/flowent/stats_service.py +218 -0
  185. package/backend/src/flowent/tools/__init__.py +201 -0
  186. package/backend/src/flowent/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  187. package/backend/src/flowent/tools/__pycache__/connect.cpython-313.pyc +0 -0
  188. package/backend/src/flowent/tools/__pycache__/contacts.cpython-313.pyc +0 -0
  189. package/backend/src/flowent/tools/__pycache__/create_agent.cpython-313.pyc +0 -0
  190. package/backend/src/flowent/tools/__pycache__/create_tab.cpython-313.pyc +0 -0
  191. package/backend/src/flowent/tools/__pycache__/delete_tab.cpython-313.pyc +0 -0
  192. package/backend/src/flowent/tools/__pycache__/edit.cpython-313.pyc +0 -0
  193. package/backend/src/flowent/tools/__pycache__/exec.cpython-313.pyc +0 -0
  194. package/backend/src/flowent/tools/__pycache__/fetch.cpython-313.pyc +0 -0
  195. package/backend/src/flowent/tools/__pycache__/idle.cpython-313.pyc +0 -0
  196. package/backend/src/flowent/tools/__pycache__/list_roles.cpython-313.pyc +0 -0
  197. package/backend/src/flowent/tools/__pycache__/list_tabs.cpython-313.pyc +0 -0
  198. package/backend/src/flowent/tools/__pycache__/list_tools.cpython-313.pyc +0 -0
  199. package/backend/src/flowent/tools/__pycache__/manage_prompts.cpython-313.pyc +0 -0
  200. package/backend/src/flowent/tools/__pycache__/manage_providers.cpython-313.pyc +0 -0
  201. package/backend/src/flowent/tools/__pycache__/manage_roles.cpython-313.pyc +0 -0
  202. package/backend/src/flowent/tools/__pycache__/manage_settings.cpython-313.pyc +0 -0
  203. package/backend/src/flowent/tools/__pycache__/mcp.cpython-313.pyc +0 -0
  204. package/backend/src/flowent/tools/__pycache__/read.cpython-313.pyc +0 -0
  205. package/backend/src/flowent/tools/__pycache__/send.cpython-313.pyc +0 -0
  206. package/backend/src/flowent/tools/__pycache__/set_permissions.cpython-313.pyc +0 -0
  207. package/backend/src/flowent/tools/__pycache__/sleep.cpython-313.pyc +0 -0
  208. package/backend/src/flowent/tools/__pycache__/todo.cpython-313.pyc +0 -0
  209. package/backend/src/flowent/tools/connect.py +156 -0
  210. package/backend/src/flowent/tools/contacts.py +22 -0
  211. package/backend/src/flowent/tools/create_agent.py +270 -0
  212. package/backend/src/flowent/tools/create_tab.py +59 -0
  213. package/backend/src/flowent/tools/delete_tab.py +39 -0
  214. package/backend/src/flowent/tools/edit.py +142 -0
  215. package/backend/src/flowent/tools/exec.py +117 -0
  216. package/backend/src/flowent/tools/fetch.py +85 -0
  217. package/backend/src/flowent/tools/idle.py +27 -0
  218. package/backend/src/flowent/tools/list_roles.py +50 -0
  219. package/backend/src/flowent/tools/list_tabs.py +96 -0
  220. package/backend/src/flowent/tools/list_tools.py +24 -0
  221. package/backend/src/flowent/tools/manage_prompts.py +102 -0
  222. package/backend/src/flowent/tools/manage_providers.py +220 -0
  223. package/backend/src/flowent/tools/manage_roles.py +275 -0
  224. package/backend/src/flowent/tools/manage_settings.py +346 -0
  225. package/backend/src/flowent/tools/mcp.py +199 -0
  226. package/backend/src/flowent/tools/read.py +152 -0
  227. package/backend/src/flowent/tools/send.py +50 -0
  228. package/backend/src/flowent/tools/set_permissions.py +84 -0
  229. package/backend/src/flowent/tools/sleep.py +41 -0
  230. package/backend/src/flowent/tools/todo.py +51 -0
  231. package/backend/src/flowent/workspace_store.py +479 -0
  232. package/backend/tests/__init__.py +0 -0
  233. package/backend/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  234. package/backend/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  235. package/backend/tests/conftest.py +6 -0
  236. package/backend/tests/integration/api/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  237. package/backend/tests/integration/api/__pycache__/test_access_api.cpython-313-pytest-9.0.3.pyc +0 -0
  238. package/backend/tests/integration/api/__pycache__/test_assistant_api.cpython-313-pytest-9.0.3.pyc +0 -0
  239. package/backend/tests/integration/api/__pycache__/test_frontend_mounting.cpython-313-pytest-9.0.3.pyc +0 -0
  240. package/backend/tests/integration/api/__pycache__/test_mcp_api.cpython-313-pytest-9.0.3.pyc +0 -0
  241. package/backend/tests/integration/api/__pycache__/test_meta_api.cpython-313-pytest-9.0.3.pyc +0 -0
  242. package/backend/tests/integration/api/__pycache__/test_nodes_api.cpython-313-pytest-9.0.3.pyc +0 -0
  243. package/backend/tests/integration/api/__pycache__/test_prompts_api.cpython-313-pytest-9.0.3.pyc +0 -0
  244. package/backend/tests/integration/api/__pycache__/test_roles_api.cpython-313-pytest-9.0.3.pyc +0 -0
  245. package/backend/tests/integration/api/__pycache__/test_tabs_api.cpython-313-pytest-9.0.3.pyc +0 -0
  246. package/backend/tests/integration/api/conftest.py +29 -0
  247. package/backend/tests/integration/api/test_access_api.py +182 -0
  248. package/backend/tests/integration/api/test_assistant_api.py +354 -0
  249. package/backend/tests/integration/api/test_frontend_mounting.py +61 -0
  250. package/backend/tests/integration/api/test_mcp_api.py +116 -0
  251. package/backend/tests/integration/api/test_meta_api.py +33 -0
  252. package/backend/tests/integration/api/test_nodes_api.py +486 -0
  253. package/backend/tests/integration/api/test_prompts_api.py +47 -0
  254. package/backend/tests/integration/api/test_roles_api.py +227 -0
  255. package/backend/tests/integration/api/test_tabs_api.py +501 -0
  256. package/backend/tests/unit/__pycache__/test_access.cpython-313-pytest-9.0.3.pyc +0 -0
  257. package/backend/tests/unit/__pycache__/test_cli.cpython-313-pytest-9.0.3.pyc +0 -0
  258. package/backend/tests/unit/__pycache__/test_graph_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  259. package/backend/tests/unit/__pycache__/test_network.cpython-313-pytest-9.0.3.pyc +0 -0
  260. package/backend/tests/unit/__pycache__/test_state_sqlite_storage.cpython-313-pytest-9.0.3.pyc +0 -0
  261. package/backend/tests/unit/__pycache__/test_workspace_store.cpython-313-pytest-9.0.3.pyc +0 -0
  262. package/backend/tests/unit/agent/__pycache__/test_agent_public_api.cpython-313-pytest-9.0.3.pyc +0 -0
  263. package/backend/tests/unit/agent/__pycache__/test_agent_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  264. package/backend/tests/unit/agent/test_agent_public_api.py +746 -0
  265. package/backend/tests/unit/agent/test_agent_runtime.py +2726 -0
  266. package/backend/tests/unit/channels/__pycache__/test_telegram_channel.cpython-313-pytest-9.0.3.pyc +0 -0
  267. package/backend/tests/unit/channels/test_telegram_channel.py +552 -0
  268. package/backend/tests/unit/logging/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
  269. package/backend/tests/unit/logging/test_logging.py +132 -0
  270. package/backend/tests/unit/prompts/__pycache__/test_prompts.cpython-313-pytest-9.0.3.pyc +0 -0
  271. package/backend/tests/unit/prompts/test_prompts.py +569 -0
  272. package/backend/tests/unit/providers/__pycache__/test_anthropic_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  273. package/backend/tests/unit/providers/__pycache__/test_errors.cpython-313-pytest-9.0.3.pyc +0 -0
  274. package/backend/tests/unit/providers/__pycache__/test_extract_delta_parts.cpython-313-pytest-9.0.3.pyc +0 -0
  275. package/backend/tests/unit/providers/__pycache__/test_openai_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  276. package/backend/tests/unit/providers/__pycache__/test_openai_responses.cpython-313-pytest-9.0.3.pyc +0 -0
  277. package/backend/tests/unit/providers/__pycache__/test_provider_gateway.cpython-313-pytest-9.0.3.pyc +0 -0
  278. package/backend/tests/unit/providers/__pycache__/test_think_tag_parser.cpython-313-pytest-9.0.3.pyc +0 -0
  279. package/backend/tests/unit/providers/test_anthropic_provider.py +185 -0
  280. package/backend/tests/unit/providers/test_errors.py +68 -0
  281. package/backend/tests/unit/providers/test_extract_delta_parts.py +22 -0
  282. package/backend/tests/unit/providers/test_openai_provider.py +139 -0
  283. package/backend/tests/unit/providers/test_openai_responses.py +402 -0
  284. package/backend/tests/unit/providers/test_provider_gateway.py +359 -0
  285. package/backend/tests/unit/providers/test_think_tag_parser.py +36 -0
  286. package/backend/tests/unit/routes/__pycache__/test_prompts_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  287. package/backend/tests/unit/routes/__pycache__/test_providers_route.cpython-313-pytest-9.0.3.pyc +0 -0
  288. package/backend/tests/unit/routes/__pycache__/test_roles_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  289. package/backend/tests/unit/routes/__pycache__/test_settings_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  290. package/backend/tests/unit/routes/__pycache__/test_stats_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  291. package/backend/tests/unit/routes/test_prompts_routes.py +104 -0
  292. package/backend/tests/unit/routes/test_providers_route.py +368 -0
  293. package/backend/tests/unit/routes/test_roles_routes.py +426 -0
  294. package/backend/tests/unit/routes/test_settings_routes.py +1138 -0
  295. package/backend/tests/unit/routes/test_stats_routes.py +149 -0
  296. package/backend/tests/unit/runtime/__pycache__/test_bootstrap_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  297. package/backend/tests/unit/runtime/test_bootstrap_runtime.py +1012 -0
  298. package/backend/tests/unit/sandbox/__pycache__/test_sandbox_tools.cpython-313-pytest-9.0.3.pyc +0 -0
  299. package/backend/tests/unit/sandbox/test_sandbox_tools.py +78 -0
  300. package/backend/tests/unit/security/__pycache__/test_security.cpython-313-pytest-9.0.3.pyc +0 -0
  301. package/backend/tests/unit/security/test_security.py +110 -0
  302. package/backend/tests/unit/settings/__pycache__/test_settings_roles.cpython-313-pytest-9.0.3.pyc +0 -0
  303. package/backend/tests/unit/settings/test_settings_roles.py +711 -0
  304. package/backend/tests/unit/test_access.py +45 -0
  305. package/backend/tests/unit/test_cli.py +124 -0
  306. package/backend/tests/unit/test_graph_runtime.py +72 -0
  307. package/backend/tests/unit/test_network.py +51 -0
  308. package/backend/tests/unit/test_state_sqlite_storage.py +93 -0
  309. package/backend/tests/unit/test_workspace_store.py +231 -0
  310. package/backend/tests/unit/tools/__pycache__/test_connect_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  311. package/backend/tests/unit/tools/__pycache__/test_create_agent_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  312. package/backend/tests/unit/tools/__pycache__/test_delete_tab_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  313. package/backend/tests/unit/tools/__pycache__/test_edit_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  314. package/backend/tests/unit/tools/__pycache__/test_exec_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  315. package/backend/tests/unit/tools/__pycache__/test_fetch_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  316. package/backend/tests/unit/tools/__pycache__/test_manage_prompts_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  317. package/backend/tests/unit/tools/__pycache__/test_manage_providers_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  318. package/backend/tests/unit/tools/__pycache__/test_manage_roles_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  319. package/backend/tests/unit/tools/__pycache__/test_manage_settings_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  320. package/backend/tests/unit/tools/__pycache__/test_read_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  321. package/backend/tests/unit/tools/__pycache__/test_set_permissions_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  322. package/backend/tests/unit/tools/__pycache__/test_todo_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  323. package/backend/tests/unit/tools/__pycache__/test_tool_registry.cpython-313-pytest-9.0.3.pyc +0 -0
  324. package/backend/tests/unit/tools/test_connect_tool.py +229 -0
  325. package/backend/tests/unit/tools/test_create_agent_tool.py +524 -0
  326. package/backend/tests/unit/tools/test_delete_tab_tool.py +83 -0
  327. package/backend/tests/unit/tools/test_edit_tool.py +115 -0
  328. package/backend/tests/unit/tools/test_exec_tool.py +81 -0
  329. package/backend/tests/unit/tools/test_fetch_tool.py +65 -0
  330. package/backend/tests/unit/tools/test_manage_prompts_tool.py +117 -0
  331. package/backend/tests/unit/tools/test_manage_providers_tool.py +458 -0
  332. package/backend/tests/unit/tools/test_manage_roles_tool.py +411 -0
  333. package/backend/tests/unit/tools/test_manage_settings_tool.py +608 -0
  334. package/backend/tests/unit/tools/test_read_tool.py +33 -0
  335. package/backend/tests/unit/tools/test_set_permissions_tool.py +391 -0
  336. package/backend/tests/unit/tools/test_todo_tool.py +37 -0
  337. package/backend/tests/unit/tools/test_tool_registry.py +91 -0
  338. package/backend/uv.lock +1144 -0
  339. package/bin/flowent.mjs +62 -35
  340. package/dist/frontend/assets/AssistantPage-B3Xc08AS.js +1 -0
  341. package/dist/frontend/assets/ChannelsPage-ByLd28xk.js +1 -0
  342. package/dist/frontend/assets/HomePage-C0hAx9_l.js +3 -0
  343. package/dist/frontend/assets/McpPage-DkrYLvBv.js +7 -0
  344. package/dist/frontend/assets/PageScaffold-D4jO9ooX.js +1 -0
  345. package/dist/frontend/assets/PromptsPage-DWA7rRJd.js +1 -0
  346. package/dist/frontend/assets/ProvidersPage-PUWT8seJ.js +3 -0
  347. package/dist/frontend/assets/RolesPage-CqcclGRw.js +1 -0
  348. package/dist/frontend/assets/SettingsPage-8tS2cJgX.js +3 -0
  349. package/dist/frontend/assets/StatsPage-BX9khYzu.js +1 -0
  350. package/dist/frontend/assets/ToolsPage-9Tl9FdeD.js +1 -0
  351. package/dist/frontend/assets/WorkspaceCommandDialog-CCXxjDL8.js +1 -0
  352. package/dist/frontend/assets/WorkspacePanels-aMdJ7ZH7.js +1 -0
  353. package/dist/frontend/assets/alert-dialog-kFYVQ7oX.js +1 -0
  354. package/dist/frontend/assets/badge-74-3jsCg.js +1 -0
  355. package/dist/frontend/assets/constants-XUzFf6i1.js +1 -0
  356. package/dist/frontend/assets/datetime-m6_O_Ci9.js +1 -0
  357. package/dist/frontend/assets/dialog-BeGSweF6.js +1 -0
  358. package/dist/frontend/assets/elk-worker.min-C9JGDOE-.js +6312 -0
  359. package/dist/frontend/assets/graph-vendor-CHpVij2M.css +1 -0
  360. package/dist/frontend/assets/graph-vendor-DRq_-6fV.js +7 -0
  361. package/dist/frontend/assets/index-BHC1Vhy8.css +1 -0
  362. package/dist/frontend/assets/index-CL1ALZ3r.js +10 -0
  363. package/dist/frontend/assets/layout.worker-jMHqAFbP.js +24 -0
  364. package/dist/frontend/assets/markdown-vendor-DVdy_w12.js +29 -0
  365. package/dist/frontend/assets/modelParams-CaHd0903.js +1 -0
  366. package/dist/frontend/assets/react-vendor-mEs_JJxa.js +9 -0
  367. package/dist/frontend/assets/roles-2OLDeTc5.js +1 -0
  368. package/dist/frontend/assets/rolldown-runtime-BYbx6iT9.js +1 -0
  369. package/dist/frontend/assets/select-DL_LPeDj.js +1 -0
  370. package/dist/frontend/assets/shared-CMxbpLeQ.js +1 -0
  371. package/dist/frontend/assets/triState-DEr3NkXV.js +1 -0
  372. package/dist/frontend/assets/ui-vendor-Dg9NNnWX.js +51 -0
  373. package/dist/frontend/index.html +36 -0
  374. package/package.json +28 -41
  375. package/dist/.next/BUILD_ID +0 -1
  376. package/dist/.next/app-path-routes-manifest.json +0 -6
  377. package/dist/.next/build-manifest.json +0 -20
  378. package/dist/.next/package.json +0 -1
  379. package/dist/.next/prerender-manifest.json +0 -114
  380. package/dist/.next/required-server-files.json +0 -333
  381. package/dist/.next/routes-manifest.json +0 -69
  382. package/dist/.next/server/app/_global-error/page/app-paths-manifest.json +0 -3
  383. package/dist/.next/server/app/_global-error/page/build-manifest.json +0 -16
  384. package/dist/.next/server/app/_global-error/page/next-font-manifest.json +0 -6
  385. package/dist/.next/server/app/_global-error/page/react-loadable-manifest.json +0 -1
  386. package/dist/.next/server/app/_global-error/page/server-reference-manifest.json +0 -4
  387. package/dist/.next/server/app/_global-error/page.js +0 -9
  388. package/dist/.next/server/app/_global-error/page.js.map +0 -5
  389. package/dist/.next/server/app/_global-error/page.js.nft.json +0 -1
  390. package/dist/.next/server/app/_global-error/page_client-reference-manifest.js +0 -3
  391. package/dist/.next/server/app/_global-error.html +0 -1
  392. package/dist/.next/server/app/_global-error.meta +0 -15
  393. package/dist/.next/server/app/_global-error.rsc +0 -14
  394. package/dist/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +0 -5
  395. package/dist/.next/server/app/_global-error.segments/_full.segment.rsc +0 -14
  396. package/dist/.next/server/app/_global-error.segments/_head.segment.rsc +0 -5
  397. package/dist/.next/server/app/_global-error.segments/_index.segment.rsc +0 -5
  398. package/dist/.next/server/app/_global-error.segments/_tree.segment.rsc +0 -1
  399. package/dist/.next/server/app/_not-found/page/app-paths-manifest.json +0 -3
  400. package/dist/.next/server/app/_not-found/page/build-manifest.json +0 -16
  401. package/dist/.next/server/app/_not-found/page/next-font-manifest.json +0 -10
  402. package/dist/.next/server/app/_not-found/page/react-loadable-manifest.json +0 -1
  403. package/dist/.next/server/app/_not-found/page/server-reference-manifest.json +0 -4
  404. package/dist/.next/server/app/_not-found/page.js +0 -13
  405. package/dist/.next/server/app/_not-found/page.js.map +0 -5
  406. package/dist/.next/server/app/_not-found/page.js.nft.json +0 -1
  407. package/dist/.next/server/app/_not-found/page_client-reference-manifest.js +0 -3
  408. package/dist/.next/server/app/_not-found.html +0 -1
  409. package/dist/.next/server/app/_not-found.meta +0 -16
  410. package/dist/.next/server/app/_not-found.rsc +0 -16
  411. package/dist/.next/server/app/_not-found.segments/_full.segment.rsc +0 -16
  412. package/dist/.next/server/app/_not-found.segments/_head.segment.rsc +0 -6
  413. package/dist/.next/server/app/_not-found.segments/_index.segment.rsc +0 -5
  414. package/dist/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +0 -5
  415. package/dist/.next/server/app/_not-found.segments/_not-found.segment.rsc +0 -5
  416. package/dist/.next/server/app/_not-found.segments/_tree.segment.rsc +0 -2
  417. package/dist/.next/server/app/icon.svg/route/app-paths-manifest.json +0 -3
  418. package/dist/.next/server/app/icon.svg/route/build-manifest.json +0 -9
  419. package/dist/.next/server/app/icon.svg/route.js +0 -6
  420. package/dist/.next/server/app/icon.svg/route.js.map +0 -5
  421. package/dist/.next/server/app/icon.svg/route.js.nft.json +0 -1
  422. package/dist/.next/server/app/icon.svg.meta +0 -1
  423. package/dist/.next/server/app/index.html +0 -1
  424. package/dist/.next/server/app/index.meta +0 -14
  425. package/dist/.next/server/app/index.rsc +0 -15
  426. package/dist/.next/server/app/index.segments/__PAGE__.segment.rsc +0 -5
  427. package/dist/.next/server/app/index.segments/_full.segment.rsc +0 -15
  428. package/dist/.next/server/app/index.segments/_head.segment.rsc +0 -6
  429. package/dist/.next/server/app/index.segments/_index.segment.rsc +0 -5
  430. package/dist/.next/server/app/index.segments/_tree.segment.rsc +0 -3
  431. package/dist/.next/server/app/page/app-paths-manifest.json +0 -3
  432. package/dist/.next/server/app/page/build-manifest.json +0 -16
  433. package/dist/.next/server/app/page/next-font-manifest.json +0 -10
  434. package/dist/.next/server/app/page/react-loadable-manifest.json +0 -1
  435. package/dist/.next/server/app/page/server-reference-manifest.json +0 -4
  436. package/dist/.next/server/app/page.js +0 -14
  437. package/dist/.next/server/app/page.js.map +0 -5
  438. package/dist/.next/server/app/page.js.nft.json +0 -1
  439. package/dist/.next/server/app/page_client-reference-manifest.js +0 -3
  440. package/dist/.next/server/app-paths-manifest.json +0 -6
  441. package/dist/.next/server/chunks/[externals]_next_dist_0arv.vj._.js +0 -3
  442. package/dist/.next/server/chunks/[root-of-the-server]__0vcj1q1._.js +0 -13
  443. package/dist/.next/server/chunks/[turbopack]_runtime.js +0 -903
  444. package/dist/.next/server/chunks/_next-internal_server_app_icon_svg_route_actions_0-0ehc~.js +0 -3
  445. package/dist/.next/server/chunks/ssr/05w9_next_dist_0ihu0u9._.js +0 -6
  446. package/dist/.next/server/chunks/ssr/05w9_next_dist_client_components_12u3mib._.js +0 -3
  447. package/dist/.next/server/chunks/ssr/05w9_next_dist_client_components_builtin_forbidden_04fbe_..js +0 -3
  448. package/dist/.next/server/chunks/ssr/05w9_next_dist_client_components_builtin_global-error_0brpl_..js +0 -3
  449. package/dist/.next/server/chunks/ssr/05w9_next_dist_client_components_builtin_unauthorized_0~2g66g.js +0 -3
  450. package/dist/.next/server/chunks/ssr/05w9_next_dist_esm_build_templates_app-page_0~cyr1_.js +0 -4
  451. package/dist/.next/server/chunks/ssr/05w9_next_dist_esm_build_templates_app-page_1105emf.js +0 -4
  452. package/dist/.next/server/chunks/ssr/05w9_next_dist_esm_build_templates_app-page_11uhyqv.js +0 -4
  453. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0.t9_75._.js +0 -33
  454. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0c0ud_z._.js +0 -3
  455. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0f9_8d4._.js +0 -3
  456. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0l5ko41._.js +0 -19
  457. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0mn6z7i._.js +0 -3
  458. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0npxxst._.js +0 -33
  459. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0qjhaca._.js +0 -3
  460. package/dist/.next/server/chunks/ssr/[root-of-the-server]__0rwgw3s._.js +0 -3
  461. package/dist/.next/server/chunks/ssr/[turbopack]_runtime.js +0 -903
  462. package/dist/.next/server/chunks/ssr/_next-internal_server_app__global-error_page_actions_0k77kol.js +0 -3
  463. package/dist/.next/server/chunks/ssr/_next-internal_server_app__not-found_page_actions_0eq97pa.js +0 -3
  464. package/dist/.next/server/chunks/ssr/_next-internal_server_app_page_actions_09-gtaw.js +0 -3
  465. package/dist/.next/server/chunks/ssr/node_modules__pnpm_056~6.6._.js +0 -3
  466. package/dist/.next/server/chunks/ssr/node_modules__pnpm_0~j0k.e._.js +0 -33
  467. package/dist/.next/server/functions-config-manifest.json +0 -4
  468. package/dist/.next/server/middleware-build-manifest.js +0 -20
  469. package/dist/.next/server/middleware-manifest.json +0 -6
  470. package/dist/.next/server/next-font-manifest.js +0 -1
  471. package/dist/.next/server/next-font-manifest.json +0 -13
  472. package/dist/.next/server/pages/404.html +0 -1
  473. package/dist/.next/server/pages/500.html +0 -1
  474. package/dist/.next/server/pages-manifest.json +0 -4
  475. package/dist/.next/server/prefetch-hints.json +0 -1
  476. package/dist/.next/server/server-reference-manifest.js +0 -1
  477. package/dist/.next/server/server-reference-manifest.json +0 -5
  478. package/dist/.next/static/7FFlzRe2eS-D0Lw5oEpmC/_buildManifest.js +0 -11
  479. package/dist/.next/static/7FFlzRe2eS-D0Lw5oEpmC/_clientMiddlewareManifest.js +0 -1
  480. package/dist/.next/static/7FFlzRe2eS-D0Lw5oEpmC/_ssgManifest.js +0 -1
  481. package/dist/.next/static/chunks/01qk2~bgf76vu.js +0 -1
  482. package/dist/.next/static/chunks/03~yq9q893hmn.js +0 -1
  483. package/dist/.next/static/chunks/080queev.r2uy.js +0 -31
  484. package/dist/.next/static/chunks/0v3lyuj75aq50.js +0 -1
  485. package/dist/.next/static/chunks/10b~xdx5c-i7s.js +0 -5
  486. package/dist/.next/static/chunks/15~9l5n.~r-.4.css +0 -2
  487. package/dist/.next/static/chunks/turbopack-0m-970~qvs7sc.js +0 -1
  488. package/dist/.next/static/media/7178b3e590c64307-s.11.cyxs5p-0z~.woff2 +0 -0
  489. package/dist/.next/static/media/8a480f0b521d4e75-s.06d3mdzz5bre_.woff2 +0 -0
  490. package/dist/.next/static/media/caa3a2e1cccd8315-s.p.16t1db8_9y2o~.woff2 +0 -0
  491. package/dist/package.json +0 -87
  492. package/dist/server.js +0 -38
  493. /package/{dist/.next/server/app/icon.svg.body → backend/src/flowent/static/favicon.svg} +0 -0
  494. /package/dist/{.next/static/media/icon.0.r~afrtrocz9.svg → frontend/favicon.svg} +0 -0
@@ -0,0 +1,85 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from collections.abc import Callable
5
+ from typing import TYPE_CHECKING, Any, ClassVar
6
+
7
+ from loguru import logger
8
+
9
+ from flowent.network import create_http_session, iter_response_text
10
+ from flowent.tools import Tool, re_raise_interrupt
11
+
12
+ if TYPE_CHECKING:
13
+ from flowent.agent import Agent
14
+
15
+
16
+ class FetchTool(Tool):
17
+ name = "fetch"
18
+ description = "Make an HTTP request."
19
+ parameters: ClassVar[dict[str, Any]] = {
20
+ "type": "object",
21
+ "properties": {
22
+ "method": {
23
+ "type": "string",
24
+ "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"],
25
+ "description": "HTTP method",
26
+ },
27
+ "url": {"type": "string", "description": "Request URL"},
28
+ "headers": {
29
+ "type": "object",
30
+ "description": "Request headers (optional)",
31
+ },
32
+ "body": {"type": "string", "description": "Request body (optional)"},
33
+ },
34
+ "required": ["method", "url"],
35
+ }
36
+
37
+ def execute(self, agent: Agent, args: dict[str, Any], **kwargs: Any) -> str:
38
+ method = args["method"]
39
+ url = args["url"]
40
+ on_output: Callable[[str], None] | None = kwargs.get("on_output")
41
+ logger.debug("HTTP {} {}", method, url)
42
+ try:
43
+ with (
44
+ create_http_session(timeout=30.0, impersonate_browser=True) as client,
45
+ client.stream(
46
+ method,
47
+ url,
48
+ headers=args.get("headers"),
49
+ data=args.get("body"),
50
+ ) as response,
51
+ ):
52
+ close_response = getattr(response, "close", None)
53
+ agent.set_interrupt_callback(
54
+ close_response if callable(close_response) else None
55
+ )
56
+ if on_output is not None:
57
+ on_output(f"{method} {url}\n")
58
+ on_output(f"HTTP {response.status_code}\n\n")
59
+
60
+ remaining = 5000
61
+ body_parts: list[str] = []
62
+ for chunk in iter_response_text(response):
63
+ if not chunk or remaining <= 0:
64
+ continue
65
+ if len(chunk) > remaining:
66
+ chunk = chunk[:remaining]
67
+ body_parts.append(chunk)
68
+ remaining -= len(chunk)
69
+ if on_output is not None:
70
+ on_output(chunk)
71
+
72
+ body = "".join(body_parts)
73
+ logger.debug("HTTP {} {} -> {}", method, url, response.status_code)
74
+ return json.dumps(
75
+ {
76
+ "status_code": response.status_code,
77
+ "body": body,
78
+ }
79
+ )
80
+ except Exception as e:
81
+ re_raise_interrupt(agent, e)
82
+ logger.warning("HTTP request failed: {} {} - {}", method, url, e)
83
+ return json.dumps({"error": str(e)})
84
+ finally:
85
+ agent.set_interrupt_callback(None)
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, ClassVar
4
+
5
+ from flowent.tools import Tool
6
+
7
+ if TYPE_CHECKING:
8
+ from flowent.agent import Agent
9
+
10
+
11
+ class IdleTool(Tool):
12
+ name = "idle"
13
+ description = (
14
+ "Enter idle state. The agent suspends execution until a wake signal re-activates it. "
15
+ "Use this when the current step or task is finished, paused, or blocked, and there is no "
16
+ "immediate next action to take right now. Incoming messages will automatically re-activate you as new input messages, and the tool returns the idle duration when execution resumes."
17
+ )
18
+ parameters: ClassVar[dict[str, Any]] = {
19
+ "type": "object",
20
+ "properties": {},
21
+ }
22
+
23
+ def execute(self, agent: Agent, args: dict[str, Any], **kwargs: Any) -> str:
24
+ tool_call_id = kwargs.get("tool_call_id")
25
+ if tool_call_id is not None and not isinstance(tool_call_id, str):
26
+ tool_call_id = None
27
+ return agent.request_idle(tool_call_id=tool_call_id)
@@ -0,0 +1,50 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import TYPE_CHECKING, Any, ClassVar
5
+
6
+ from flowent.tools import Tool
7
+
8
+ if TYPE_CHECKING:
9
+ from flowent.agent import Agent
10
+
11
+
12
+ class ListRolesTool(Tool):
13
+ name = "list_roles"
14
+ description = "List all registered roles with builtin and optional tool views."
15
+ parameters: ClassVar[dict[str, Any]] = {
16
+ "type": "object",
17
+ "properties": {},
18
+ "required": [],
19
+ }
20
+
21
+ def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
22
+ from flowent.settings import get_settings, normalize_tool_names
23
+ from flowent.tools import MINIMUM_TOOLS, build_tool_registry
24
+
25
+ settings = get_settings()
26
+ tool_registry = build_tool_registry()
27
+ all_tool_names = [
28
+ tool.name for tool in tool_registry.list_tools(agent_visible_only=True)
29
+ ]
30
+ payload: list[dict[str, object]] = []
31
+
32
+ for role in settings.roles:
33
+ builtin_tools = normalize_tool_names([*MINIMUM_TOOLS, *role.included_tools])
34
+ optional_tools = [
35
+ tool_name
36
+ for tool_name in all_tool_names
37
+ if tool_name not in builtin_tools
38
+ and tool_name not in role.excluded_tools
39
+ ]
40
+ payload.append(
41
+ {
42
+ "name": role.name,
43
+ "description": role.description,
44
+ "system_prompt": role.system_prompt,
45
+ "builtin_tools": builtin_tools,
46
+ "optional_tools": optional_tools,
47
+ }
48
+ )
49
+
50
+ return json.dumps(payload)
@@ -0,0 +1,96 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import TYPE_CHECKING, Any, ClassVar
5
+
6
+ from flowent.graph_service import (
7
+ is_tab_leader,
8
+ list_tab_edges,
9
+ list_tab_nodes,
10
+ list_workflow_nodes,
11
+ serialize_tab_summary,
12
+ )
13
+ from flowent.tools import Tool
14
+ from flowent.workspace_store import workspace_store
15
+
16
+ if TYPE_CHECKING:
17
+ from flowent.agent import Agent
18
+
19
+
20
+ class ListTabsTool(Tool):
21
+ name = "list_workflows"
22
+ description = "List persistent workflows. Optionally include the full Workflow Graph for one workflow."
23
+ parameters: ClassVar[dict[str, Any]] = {
24
+ "type": "object",
25
+ "properties": {
26
+ "workflow_id": {
27
+ "type": "string",
28
+ "description": "Optional workflow ID to inspect in detail",
29
+ }
30
+ },
31
+ "required": [],
32
+ }
33
+
34
+ def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
35
+ workflow_id = args.get("workflow_id")
36
+ if workflow_id is not None and not isinstance(workflow_id, str):
37
+ return json.dumps({"error": "workflow_id must be a string"})
38
+
39
+ if isinstance(workflow_id, str) and workflow_id.strip():
40
+ tab = workspace_store.get_tab(workflow_id.strip())
41
+ if tab is None:
42
+ return json.dumps(
43
+ {"error": f"Workflow '{workflow_id.strip()}' not found"}
44
+ )
45
+ nodes = [
46
+ {
47
+ "id": node.id,
48
+ "node_type": node.type.value,
49
+ "name": (
50
+ node.config["name"]
51
+ if isinstance(node.config.get("name"), str)
52
+ else None
53
+ ),
54
+ "role_name": (
55
+ node.config["role_name"]
56
+ if isinstance(node.config.get("role_name"), str)
57
+ else None
58
+ ),
59
+ "is_leader": False,
60
+ "position": (
61
+ tab.definition.view.positions[node.id].serialize()
62
+ if node.id in tab.definition.view.positions
63
+ else None
64
+ ),
65
+ }
66
+ for node in list_workflow_nodes(tab.id)
67
+ ]
68
+ return json.dumps(
69
+ {
70
+ "workflow": serialize_tab_summary(tab),
71
+ "nodes": nodes,
72
+ "runtime_nodes": [
73
+ {
74
+ "id": node.id,
75
+ "name": node.config.name,
76
+ "role_name": node.config.role_name,
77
+ "is_leader": is_tab_leader(
78
+ node_id=node.id,
79
+ tab_id=tab.id,
80
+ ),
81
+ "state": node.state.value,
82
+ "position": (
83
+ node.position.serialize()
84
+ if node.position is not None
85
+ else None
86
+ ),
87
+ }
88
+ for node in list_tab_nodes(tab.id)
89
+ ],
90
+ "edges": [edge.serialize() for edge in list_tab_edges(tab.id)],
91
+ }
92
+ )
93
+
94
+ return json.dumps(
95
+ [serialize_tab_summary(tab) for tab in workspace_store.list_tabs()]
96
+ )
@@ -0,0 +1,24 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import TYPE_CHECKING, Any, ClassVar
5
+
6
+ from flowent.tools import Tool
7
+
8
+ if TYPE_CHECKING:
9
+ from flowent.agent import Agent
10
+
11
+
12
+ class ListToolsTool(Tool):
13
+ name = "list_tools"
14
+ description = "List all registered tools with their names and descriptions."
15
+ parameters: ClassVar[dict[str, Any]] = {
16
+ "type": "object",
17
+ "properties": {},
18
+ "required": [],
19
+ }
20
+
21
+ def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
22
+ from flowent.tools import list_agent_visible_tool_descriptors
23
+
24
+ return json.dumps(list_agent_visible_tool_descriptors())
@@ -0,0 +1,102 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from typing import TYPE_CHECKING, Any, ClassVar
5
+
6
+ if TYPE_CHECKING:
7
+ from flowent.agent import Agent
8
+
9
+ from flowent.tools import Tool
10
+
11
+
12
+ class ManagePromptsTool(Tool):
13
+ name = "manage_prompts"
14
+ description = (
15
+ "Read and update the global custom prompt and custom post prompt. The "
16
+ "custom prompt is appended to every node's system prompt, and the "
17
+ "custom post prompt is appended after the built-in runtime post prompt "
18
+ "in every runtime request tail."
19
+ )
20
+ parameters: ClassVar[dict[str, Any]] = {
21
+ "type": "object",
22
+ "properties": {
23
+ "action": {
24
+ "type": "string",
25
+ "enum": ["get", "update"],
26
+ "description": "Prompt management action",
27
+ },
28
+ "custom_prompt": {
29
+ "type": "string",
30
+ "description": "Updated global custom prompt",
31
+ },
32
+ "custom_post_prompt": {
33
+ "type": "string",
34
+ "description": "Updated global custom post prompt",
35
+ },
36
+ },
37
+ "required": ["action"],
38
+ }
39
+
40
+ def execute(self, agent: Agent, args: dict[str, Any], **_kwargs: Any) -> str:
41
+ from flowent.settings import get_settings, save_settings
42
+
43
+ action = args.get("action")
44
+ custom_prompt = args.get("custom_prompt")
45
+ custom_post_prompt = args.get("custom_post_prompt")
46
+ legacy_post_prompt = args.get("post_prompt")
47
+
48
+ if not isinstance(action, str):
49
+ return json.dumps({"error": "action must be a string"})
50
+
51
+ settings = get_settings()
52
+
53
+ if action == "get":
54
+ return json.dumps(
55
+ {
56
+ "custom_prompt": settings.custom_prompt,
57
+ "custom_post_prompt": settings.custom_post_prompt,
58
+ }
59
+ )
60
+
61
+ if action != "update":
62
+ return json.dumps({"error": f"Unsupported action: {action}"})
63
+
64
+ if (
65
+ "custom_prompt" not in args
66
+ and "custom_post_prompt" not in args
67
+ and "post_prompt" not in args
68
+ ):
69
+ return json.dumps(
70
+ {"error": "custom_prompt or custom_post_prompt is required"}
71
+ )
72
+ if "custom_prompt" in args and not isinstance(custom_prompt, str):
73
+ return json.dumps({"error": "custom_prompt must be a string"})
74
+ if "custom_post_prompt" in args and not isinstance(custom_post_prompt, str):
75
+ return json.dumps({"error": "custom_post_prompt must be a string"})
76
+ if (
77
+ "post_prompt" in args
78
+ and "custom_post_prompt" not in args
79
+ and not isinstance(legacy_post_prompt, str)
80
+ ):
81
+ return json.dumps({"error": "custom_post_prompt must be a string"})
82
+
83
+ next_custom_prompt = custom_prompt if isinstance(custom_prompt, str) else None
84
+ next_custom_post_prompt = (
85
+ custom_post_prompt
86
+ if isinstance(custom_post_prompt, str)
87
+ else legacy_post_prompt
88
+ if isinstance(legacy_post_prompt, str)
89
+ else None
90
+ )
91
+
92
+ if next_custom_prompt is not None:
93
+ settings.custom_prompt = next_custom_prompt
94
+ if next_custom_post_prompt is not None:
95
+ settings.custom_post_prompt = next_custom_post_prompt
96
+ save_settings(settings)
97
+ return json.dumps(
98
+ {
99
+ "custom_prompt": settings.custom_prompt,
100
+ "custom_post_prompt": settings.custom_post_prompt,
101
+ }
102
+ )
@@ -0,0 +1,220 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from collections.abc import Callable
5
+ from typing import TYPE_CHECKING, Any, ClassVar
6
+
7
+ from loguru import logger
8
+
9
+ if TYPE_CHECKING:
10
+ from flowent.agent import Agent
11
+
12
+ from flowent.providers.configuration import serialize_discovered_model_catalog_entry
13
+ from flowent.providers.management import (
14
+ ProviderNotFoundError,
15
+ create_provider_entry,
16
+ delete_provider_entry,
17
+ list_provider_payloads,
18
+ update_provider_entry,
19
+ )
20
+ from flowent.settings import (
21
+ build_provider_headers,
22
+ build_provider_retry_429_delay_seconds,
23
+ )
24
+ from flowent.tools import Tool, re_raise_interrupt
25
+
26
+
27
+ class ManageProvidersTool(Tool):
28
+ name = "manage_providers"
29
+ description = (
30
+ "Manage LLM provider configuration. Supports listing, creating, "
31
+ "updating, deleting providers, and listing models for a provider."
32
+ )
33
+ parameters: ClassVar[dict[str, Any]] = {
34
+ "type": "object",
35
+ "properties": {
36
+ "action": {
37
+ "type": "string",
38
+ "enum": ["list", "create", "update", "delete", "list_models"],
39
+ "description": "Provider management action",
40
+ },
41
+ "id": {
42
+ "type": "string",
43
+ "description": "Target provider ID for update, delete, or list_models",
44
+ },
45
+ "name": {
46
+ "type": "string",
47
+ "description": "Provider display name",
48
+ },
49
+ "type": {
50
+ "type": "string",
51
+ "description": "Provider type, such as openai_compatible",
52
+ },
53
+ "base_url": {
54
+ "type": "string",
55
+ "description": "Provider API base URL",
56
+ },
57
+ "api_key": {
58
+ "type": "string",
59
+ "description": "Provider API key",
60
+ },
61
+ "headers": {
62
+ "type": "object",
63
+ "description": "Provider request header overrides",
64
+ "additionalProperties": {"type": "string"},
65
+ },
66
+ "retry_429_delay_seconds": {
67
+ "type": "integer",
68
+ "description": "Extra wait time in seconds after HTTP 429 before retrying",
69
+ },
70
+ },
71
+ "required": ["action"],
72
+ }
73
+
74
+ def execute(self, agent: Agent, args: dict[str, Any], **kwargs: Any) -> str:
75
+ from flowent.providers.gateway import gateway
76
+ from flowent.settings import get_settings, save_settings
77
+
78
+ on_output: Callable[[str], None] | None = kwargs.get("on_output")
79
+ action = args.get("action")
80
+ provider_id = args.get("id")
81
+ name = args.get("name")
82
+ provider_type = args.get("type")
83
+ base_url = args.get("base_url")
84
+ api_key = args.get("api_key")
85
+ raw_headers = args.get("headers")
86
+ retry_429_delay_seconds = args.get("retry_429_delay_seconds")
87
+
88
+ if not isinstance(action, str):
89
+ return json.dumps({"error": "action must be a string"})
90
+
91
+ if provider_id is not None and not isinstance(provider_id, str):
92
+ return json.dumps({"error": "id must be a string"})
93
+ if name is not None and not isinstance(name, str):
94
+ return json.dumps({"error": "name must be a string"})
95
+ if provider_type is not None and not isinstance(provider_type, str):
96
+ return json.dumps({"error": "type must be a string"})
97
+ if base_url is not None and not isinstance(base_url, str):
98
+ return json.dumps({"error": "base_url must be a string"})
99
+ if api_key is not None and not isinstance(api_key, str):
100
+ return json.dumps({"error": "api_key must be a string"})
101
+ if raw_headers is not None and not isinstance(raw_headers, dict):
102
+ return json.dumps({"error": "headers must be a JSON object"})
103
+ if retry_429_delay_seconds is not None:
104
+ try:
105
+ build_provider_retry_429_delay_seconds(retry_429_delay_seconds)
106
+ except ValueError as exc:
107
+ return json.dumps({"error": str(exc)})
108
+
109
+ try:
110
+ headers = (
111
+ build_provider_headers(raw_headers) if raw_headers is not None else None
112
+ )
113
+ except ValueError as exc:
114
+ return json.dumps({"error": str(exc)})
115
+
116
+ settings = get_settings()
117
+
118
+ if action == "list":
119
+ return json.dumps(list_provider_payloads(settings, include_api_key=False))
120
+
121
+ if action == "create":
122
+ if not isinstance(name, str) or not name.strip():
123
+ return json.dumps({"error": "name is required"})
124
+ if not isinstance(provider_type, str) or not provider_type.strip():
125
+ return json.dumps({"error": "type is required"})
126
+ if not isinstance(base_url, str) or not base_url.strip():
127
+ return json.dumps({"error": "base_url is required"})
128
+ try:
129
+ provider = create_provider_entry(
130
+ settings,
131
+ name=name,
132
+ provider_type=provider_type,
133
+ base_url=base_url,
134
+ api_key=api_key or "",
135
+ raw_headers=headers,
136
+ raw_retry_429_delay_seconds=(
137
+ retry_429_delay_seconds
138
+ if retry_429_delay_seconds is not None
139
+ else 0
140
+ ),
141
+ base_url_required_message="base_url is required",
142
+ )
143
+ except ValueError as exc:
144
+ return json.dumps({"error": str(exc)})
145
+ save_settings(settings)
146
+ gateway.invalidate_cache()
147
+ return json.dumps(
148
+ list_provider_payloads(settings, include_api_key=False)[-1]
149
+ )
150
+
151
+ if action == "update":
152
+ if not isinstance(provider_id, str) or not provider_id:
153
+ return json.dumps({"error": "id is required"})
154
+
155
+ try:
156
+ provider = update_provider_entry(
157
+ settings,
158
+ provider_id,
159
+ name=name,
160
+ provider_type=provider_type,
161
+ base_url=base_url,
162
+ api_key=api_key,
163
+ raw_headers=headers,
164
+ raw_retry_429_delay_seconds=retry_429_delay_seconds,
165
+ )
166
+ except ValueError as exc:
167
+ return json.dumps({"error": str(exc)})
168
+ except ProviderNotFoundError:
169
+ return json.dumps({"error": f"Provider '{provider_id}' not found"})
170
+ save_settings(settings)
171
+ gateway.invalidate_cache()
172
+ return json.dumps(
173
+ list_provider_payloads(settings, include_api_key=False)[
174
+ settings.providers.index(provider)
175
+ ]
176
+ )
177
+
178
+ if action == "delete":
179
+ if not isinstance(provider_id, str) or not provider_id:
180
+ return json.dumps({"error": "id is required"})
181
+
182
+ try:
183
+ delete_provider_entry(settings, provider_id)
184
+ except ProviderNotFoundError:
185
+ return json.dumps({"error": f"Provider '{provider_id}' not found"})
186
+ save_settings(settings)
187
+ gateway.invalidate_cache()
188
+ return json.dumps({"status": "deleted"})
189
+
190
+ if action == "list_models":
191
+ if not isinstance(provider_id, str) or not provider_id:
192
+ return json.dumps({"error": "id is required"})
193
+ try:
194
+ if on_output is not None:
195
+ on_output(f"Listing models for {provider_id}\n")
196
+ models = gateway.list_models_for(
197
+ provider_id,
198
+ register_interrupt=agent.set_interrupt_callback,
199
+ )
200
+ if on_output is not None:
201
+ for model in models:
202
+ on_output(f"{model.id}\n")
203
+ return json.dumps(
204
+ [
205
+ serialize_discovered_model_catalog_entry(model)
206
+ for model in models
207
+ ]
208
+ )
209
+ except Exception as exc:
210
+ re_raise_interrupt(agent, exc)
211
+ logger.error(
212
+ "Failed to list models for provider '{}': {}",
213
+ provider_id,
214
+ exc,
215
+ )
216
+ return json.dumps({"error": str(exc)})
217
+ finally:
218
+ agent.set_interrupt_callback(None)
219
+
220
+ return json.dumps({"error": f"Unsupported action: {action}"})