flowent 0.0.6 → 0.0.10

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 (398) hide show
  1. package/README.md +1 -4
  2. package/backend/README.md +1 -4
  3. package/backend/pyproject.toml +2 -8
  4. package/backend/src/flowent/__pycache__/__init__.cpython-313.pyc +0 -0
  5. package/backend/src/flowent/__pycache__/agent.cpython-313.pyc +0 -0
  6. package/backend/src/flowent/__pycache__/cli.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 +217 -3094
  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 +364 -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 +449 -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__/config.cpython-313.pyc +0 -0
  59. package/backend/src/flowent/__pycache__/events.cpython-313.pyc +0 -0
  60. package/backend/src/flowent/__pycache__/graph_runtime.cpython-313.pyc +0 -0
  61. package/backend/src/flowent/__pycache__/graph_service.cpython-313.pyc +0 -0
  62. package/backend/src/flowent/__pycache__/image_assets.cpython-313.pyc +0 -0
  63. package/backend/src/flowent/__pycache__/mcp_service.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 -2508
  86. package/backend/src/flowent/image_assets.py +0 -356
  87. package/backend/src/flowent/mcp_service.py +0 -1918
  88. package/backend/src/flowent/model_metadata.py +0 -102
  89. package/backend/src/flowent/models/__init__.py +0 -125
  90. package/backend/src/flowent/models/__pycache__/__init__.cpython-313.pyc +0 -0
  91. package/backend/src/flowent/models/__pycache__/agent.cpython-313.pyc +0 -0
  92. package/backend/src/flowent/models/__pycache__/base.cpython-313.pyc +0 -0
  93. package/backend/src/flowent/models/__pycache__/blueprint.cpython-313.pyc +0 -0
  94. package/backend/src/flowent/models/__pycache__/content.cpython-313.pyc +0 -0
  95. package/backend/src/flowent/models/__pycache__/delta.cpython-313.pyc +0 -0
  96. package/backend/src/flowent/models/__pycache__/event.cpython-313.pyc +0 -0
  97. package/backend/src/flowent/models/__pycache__/graph.cpython-313.pyc +0 -0
  98. package/backend/src/flowent/models/__pycache__/history.cpython-313.pyc +0 -0
  99. package/backend/src/flowent/models/__pycache__/llm.cpython-313.pyc +0 -0
  100. package/backend/src/flowent/models/__pycache__/message.cpython-313.pyc +0 -0
  101. package/backend/src/flowent/models/__pycache__/tab.cpython-313.pyc +0 -0
  102. package/backend/src/flowent/models/__pycache__/todo.cpython-313.pyc +0 -0
  103. package/backend/src/flowent/models/agent.py +0 -34
  104. package/backend/src/flowent/models/base.py +0 -24
  105. package/backend/src/flowent/models/blueprint.py +0 -176
  106. package/backend/src/flowent/models/content.py +0 -164
  107. package/backend/src/flowent/models/delta.py +0 -44
  108. package/backend/src/flowent/models/event.py +0 -51
  109. package/backend/src/flowent/models/graph.py +0 -472
  110. package/backend/src/flowent/models/history.py +0 -272
  111. package/backend/src/flowent/models/llm.py +0 -62
  112. package/backend/src/flowent/models/message.py +0 -33
  113. package/backend/src/flowent/models/tab.py +0 -85
  114. package/backend/src/flowent/models/todo.py +0 -10
  115. package/backend/src/flowent/network.py +0 -146
  116. package/backend/src/flowent/observability_service.py +0 -218
  117. package/backend/src/flowent/prompts/__init__.py +0 -67
  118. package/backend/src/flowent/prompts/__pycache__/__init__.cpython-313.pyc +0 -0
  119. package/backend/src/flowent/prompts/__pycache__/common.cpython-313.pyc +0 -0
  120. package/backend/src/flowent/prompts/__pycache__/steward.cpython-313.pyc +0 -0
  121. package/backend/src/flowent/prompts/common.py +0 -250
  122. package/backend/src/flowent/prompts/steward.py +0 -64
  123. package/backend/src/flowent/providers/__init__.py +0 -23
  124. package/backend/src/flowent/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  125. package/backend/src/flowent/providers/__pycache__/anthropic.cpython-313.pyc +0 -0
  126. package/backend/src/flowent/providers/__pycache__/base_url.cpython-313.pyc +0 -0
  127. package/backend/src/flowent/providers/__pycache__/configuration.cpython-313.pyc +0 -0
  128. package/backend/src/flowent/providers/__pycache__/content.cpython-313.pyc +0 -0
  129. package/backend/src/flowent/providers/__pycache__/errors.cpython-313.pyc +0 -0
  130. package/backend/src/flowent/providers/__pycache__/gateway.cpython-313.pyc +0 -0
  131. package/backend/src/flowent/providers/__pycache__/headers.cpython-313.pyc +0 -0
  132. package/backend/src/flowent/providers/__pycache__/management.cpython-313.pyc +0 -0
  133. package/backend/src/flowent/providers/__pycache__/openai.cpython-313.pyc +0 -0
  134. package/backend/src/flowent/providers/__pycache__/openai_responses.cpython-313.pyc +0 -0
  135. package/backend/src/flowent/providers/__pycache__/registry.cpython-313.pyc +0 -0
  136. package/backend/src/flowent/providers/__pycache__/sse.cpython-313.pyc +0 -0
  137. package/backend/src/flowent/providers/__pycache__/thinking.cpython-313.pyc +0 -0
  138. package/backend/src/flowent/providers/anthropic.py +0 -468
  139. package/backend/src/flowent/providers/base_url.py +0 -60
  140. package/backend/src/flowent/providers/configuration.py +0 -189
  141. package/backend/src/flowent/providers/content.py +0 -122
  142. package/backend/src/flowent/providers/errors.py +0 -223
  143. package/backend/src/flowent/providers/gateway.py +0 -169
  144. package/backend/src/flowent/providers/gemini.py +0 -447
  145. package/backend/src/flowent/providers/headers.py +0 -20
  146. package/backend/src/flowent/providers/management.py +0 -96
  147. package/backend/src/flowent/providers/ollama.py +0 -293
  148. package/backend/src/flowent/providers/openai.py +0 -422
  149. package/backend/src/flowent/providers/openai_responses.py +0 -655
  150. package/backend/src/flowent/providers/registry.py +0 -144
  151. package/backend/src/flowent/providers/sse.py +0 -31
  152. package/backend/src/flowent/providers/thinking.py +0 -79
  153. package/backend/src/flowent/registry.py +0 -73
  154. package/backend/src/flowent/role_management.py +0 -267
  155. package/backend/src/flowent/routes/__init__.py +0 -28
  156. package/backend/src/flowent/routes/__pycache__/__init__.cpython-313.pyc +0 -0
  157. package/backend/src/flowent/routes/__pycache__/access.cpython-313.pyc +0 -0
  158. package/backend/src/flowent/routes/__pycache__/assistant.cpython-313.pyc +0 -0
  159. package/backend/src/flowent/routes/__pycache__/image_assets.cpython-313.pyc +0 -0
  160. package/backend/src/flowent/routes/__pycache__/mcp.cpython-313.pyc +0 -0
  161. package/backend/src/flowent/routes/__pycache__/meta.cpython-313.pyc +0 -0
  162. package/backend/src/flowent/routes/__pycache__/nodes.cpython-313.pyc +0 -0
  163. package/backend/src/flowent/routes/__pycache__/prompts.cpython-313.pyc +0 -0
  164. package/backend/src/flowent/routes/__pycache__/providers_route.cpython-313.pyc +0 -0
  165. package/backend/src/flowent/routes/__pycache__/roles.cpython-313.pyc +0 -0
  166. package/backend/src/flowent/routes/__pycache__/settings.cpython-313.pyc +0 -0
  167. package/backend/src/flowent/routes/__pycache__/tabs.cpython-313.pyc +0 -0
  168. package/backend/src/flowent/routes/__pycache__/ws.cpython-313.pyc +0 -0
  169. package/backend/src/flowent/routes/access.py +0 -48
  170. package/backend/src/flowent/routes/assistant.py +0 -155
  171. package/backend/src/flowent/routes/image_assets.py +0 -33
  172. package/backend/src/flowent/routes/mcp.py +0 -125
  173. package/backend/src/flowent/routes/meta.py +0 -28
  174. package/backend/src/flowent/routes/nodes.py +0 -413
  175. package/backend/src/flowent/routes/prompts.py +0 -46
  176. package/backend/src/flowent/routes/providers_route.py +0 -365
  177. package/backend/src/flowent/routes/roles.py +0 -207
  178. package/backend/src/flowent/routes/settings.py +0 -328
  179. package/backend/src/flowent/routes/tabs.py +0 -310
  180. package/backend/src/flowent/routes/ws.py +0 -33
  181. package/backend/src/flowent/runtime.py +0 -165
  182. package/backend/src/flowent/security.py +0 -57
  183. package/backend/src/flowent/settings.py +0 -2518
  184. package/backend/src/flowent/settings_management.py +0 -298
  185. package/backend/src/flowent/state_db.py +0 -120
  186. package/backend/src/flowent/static/assets/AssistantPage-VBohhz4d.js +0 -1
  187. package/backend/src/flowent/static/assets/ChannelsPage-CIydPZA_.js +0 -1
  188. package/backend/src/flowent/static/assets/McpPage-CHPm2TPY.js +0 -7
  189. package/backend/src/flowent/static/assets/PageScaffold-DteOA8V7.js +0 -1
  190. package/backend/src/flowent/static/assets/PromptsPage-CSmJ3sZg.js +0 -1
  191. package/backend/src/flowent/static/assets/ProvidersPage-sl2jeG4e.js +0 -3
  192. package/backend/src/flowent/static/assets/RolesPage-DCe7W6Km.js +0 -1
  193. package/backend/src/flowent/static/assets/SettingsPage-Bix9e63E.js +0 -3
  194. package/backend/src/flowent/static/assets/ToolsPage-favNkj5C.js +0 -1
  195. package/backend/src/flowent/static/assets/WorkspaceCommandDialog-DRS6wiD6.js +0 -1
  196. package/backend/src/flowent/static/assets/WorkspacePage-KuaDjt_D.js +0 -3
  197. package/backend/src/flowent/static/assets/WorkspacePanels-BZxBw8M5.js +0 -1
  198. package/backend/src/flowent/static/assets/alert-dialog-DIBUCmqM.js +0 -1
  199. package/backend/src/flowent/static/assets/datetime-eJqd0V2S.js +0 -1
  200. package/backend/src/flowent/static/assets/dialog-BOvHIBrg.js +0 -1
  201. package/backend/src/flowent/static/assets/elk-worker.min-C9JGDOE-.js +0 -6312
  202. package/backend/src/flowent/static/assets/graph-vendor-CHpVij2M.css +0 -1
  203. package/backend/src/flowent/static/assets/graph-vendor-DRq_-6fV.js +0 -7
  204. package/backend/src/flowent/static/assets/index-Biio-CoI.js +0 -10
  205. package/backend/src/flowent/static/assets/index-CmQvO7sl.css +0 -1
  206. package/backend/src/flowent/static/assets/layout.worker-jMHqAFbP.js +0 -24
  207. package/backend/src/flowent/static/assets/markdown-vendor-C9RtvaJh.js +0 -29
  208. package/backend/src/flowent/static/assets/modelParams-DcEhGnu0.js +0 -1
  209. package/backend/src/flowent/static/assets/react-vendor-mEs_JJxa.js +0 -9
  210. package/backend/src/flowent/static/assets/roles-BbIEIMeG.js +0 -1
  211. package/backend/src/flowent/static/assets/rolldown-runtime-BYbx6iT9.js +0 -1
  212. package/backend/src/flowent/static/assets/select-D9SwnlXF.js +0 -1
  213. package/backend/src/flowent/static/assets/surface-Bzr1FRG4.js +0 -1
  214. package/backend/src/flowent/static/assets/triState-DgLlKdRR.js +0 -1
  215. package/backend/src/flowent/static/assets/ui-vendor-UazN8rcv.js +0 -51
  216. package/backend/src/flowent/static/favicon.svg +0 -4
  217. package/backend/src/flowent/tools/__init__.py +0 -275
  218. package/backend/src/flowent/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  219. package/backend/src/flowent/tools/__pycache__/connect.cpython-313.pyc +0 -0
  220. package/backend/src/flowent/tools/__pycache__/contacts.cpython-313.pyc +0 -0
  221. package/backend/src/flowent/tools/__pycache__/create_agent.cpython-313.pyc +0 -0
  222. package/backend/src/flowent/tools/__pycache__/create_tab.cpython-313.pyc +0 -0
  223. package/backend/src/flowent/tools/__pycache__/delete_tab.cpython-313.pyc +0 -0
  224. package/backend/src/flowent/tools/__pycache__/edit.cpython-313.pyc +0 -0
  225. package/backend/src/flowent/tools/__pycache__/exec.cpython-313.pyc +0 -0
  226. package/backend/src/flowent/tools/__pycache__/fetch.cpython-313.pyc +0 -0
  227. package/backend/src/flowent/tools/__pycache__/idle.cpython-313.pyc +0 -0
  228. package/backend/src/flowent/tools/__pycache__/list_roles.cpython-313.pyc +0 -0
  229. package/backend/src/flowent/tools/__pycache__/list_tabs.cpython-313.pyc +0 -0
  230. package/backend/src/flowent/tools/__pycache__/list_tools.cpython-313.pyc +0 -0
  231. package/backend/src/flowent/tools/__pycache__/manage_prompts.cpython-313.pyc +0 -0
  232. package/backend/src/flowent/tools/__pycache__/manage_providers.cpython-313.pyc +0 -0
  233. package/backend/src/flowent/tools/__pycache__/manage_roles.cpython-313.pyc +0 -0
  234. package/backend/src/flowent/tools/__pycache__/manage_settings.cpython-313.pyc +0 -0
  235. package/backend/src/flowent/tools/__pycache__/mcp.cpython-313.pyc +0 -0
  236. package/backend/src/flowent/tools/__pycache__/read.cpython-313.pyc +0 -0
  237. package/backend/src/flowent/tools/__pycache__/send.cpython-313.pyc +0 -0
  238. package/backend/src/flowent/tools/__pycache__/set_permissions.cpython-313.pyc +0 -0
  239. package/backend/src/flowent/tools/__pycache__/sleep.cpython-313.pyc +0 -0
  240. package/backend/src/flowent/tools/__pycache__/todo.cpython-313.pyc +0 -0
  241. package/backend/src/flowent/tools/connect.py +0 -100
  242. package/backend/src/flowent/tools/contacts.py +0 -22
  243. package/backend/src/flowent/tools/create_agent.py +0 -191
  244. package/backend/src/flowent/tools/create_tab.py +0 -61
  245. package/backend/src/flowent/tools/delete_tab.py +0 -39
  246. package/backend/src/flowent/tools/edit.py +0 -142
  247. package/backend/src/flowent/tools/exec.py +0 -118
  248. package/backend/src/flowent/tools/fetch.py +0 -85
  249. package/backend/src/flowent/tools/idle.py +0 -27
  250. package/backend/src/flowent/tools/list_roles.py +0 -75
  251. package/backend/src/flowent/tools/list_tabs.py +0 -100
  252. package/backend/src/flowent/tools/list_tools.py +0 -28
  253. package/backend/src/flowent/tools/manage_prompts.py +0 -102
  254. package/backend/src/flowent/tools/manage_providers.py +0 -220
  255. package/backend/src/flowent/tools/manage_roles.py +0 -275
  256. package/backend/src/flowent/tools/manage_settings.py +0 -364
  257. package/backend/src/flowent/tools/mcp.py +0 -199
  258. package/backend/src/flowent/tools/read.py +0 -152
  259. package/backend/src/flowent/tools/send.py +0 -68
  260. package/backend/src/flowent/tools/set_permissions.py +0 -99
  261. package/backend/src/flowent/tools/sleep.py +0 -41
  262. package/backend/src/flowent/tools/todo.py +0 -51
  263. package/backend/src/flowent/workspace_store.py +0 -479
  264. package/backend/tests/__init__.py +0 -0
  265. package/backend/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  266. package/backend/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  267. package/backend/tests/conftest.py +0 -6
  268. package/backend/tests/integration/api/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  269. package/backend/tests/integration/api/__pycache__/test_access_api.cpython-313-pytest-9.0.3.pyc +0 -0
  270. package/backend/tests/integration/api/__pycache__/test_assistant_api.cpython-313-pytest-9.0.3.pyc +0 -0
  271. package/backend/tests/integration/api/__pycache__/test_frontend_mounting.cpython-313-pytest-9.0.3.pyc +0 -0
  272. package/backend/tests/integration/api/__pycache__/test_mcp_api.cpython-313-pytest-9.0.3.pyc +0 -0
  273. package/backend/tests/integration/api/__pycache__/test_meta_api.cpython-313-pytest-9.0.3.pyc +0 -0
  274. package/backend/tests/integration/api/__pycache__/test_nodes_api.cpython-313-pytest-9.0.3.pyc +0 -0
  275. package/backend/tests/integration/api/__pycache__/test_prompts_api.cpython-313-pytest-9.0.3.pyc +0 -0
  276. package/backend/tests/integration/api/__pycache__/test_roles_api.cpython-313-pytest-9.0.3.pyc +0 -0
  277. package/backend/tests/integration/api/__pycache__/test_tabs_api.cpython-313-pytest-9.0.3.pyc +0 -0
  278. package/backend/tests/integration/api/conftest.py +0 -29
  279. package/backend/tests/integration/api/test_access_api.py +0 -182
  280. package/backend/tests/integration/api/test_assistant_api.py +0 -354
  281. package/backend/tests/integration/api/test_frontend_mounting.py +0 -61
  282. package/backend/tests/integration/api/test_mcp_api.py +0 -116
  283. package/backend/tests/integration/api/test_meta_api.py +0 -33
  284. package/backend/tests/integration/api/test_nodes_api.py +0 -722
  285. package/backend/tests/integration/api/test_prompts_api.py +0 -47
  286. package/backend/tests/integration/api/test_roles_api.py +0 -228
  287. package/backend/tests/integration/api/test_tabs_api.py +0 -802
  288. package/backend/tests/unit/__pycache__/test_access.cpython-313-pytest-9.0.3.pyc +0 -0
  289. package/backend/tests/unit/__pycache__/test_cli.cpython-313-pytest-9.0.3.pyc +0 -0
  290. package/backend/tests/unit/__pycache__/test_graph_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  291. package/backend/tests/unit/__pycache__/test_network.cpython-313-pytest-9.0.3.pyc +0 -0
  292. package/backend/tests/unit/__pycache__/test_state_sqlite_storage.cpython-313-pytest-9.0.3.pyc +0 -0
  293. package/backend/tests/unit/__pycache__/test_workspace_store.cpython-313-pytest-9.0.3.pyc +0 -0
  294. package/backend/tests/unit/agent/__pycache__/test_agent_public_api.cpython-313-pytest-9.0.3.pyc +0 -0
  295. package/backend/tests/unit/agent/__pycache__/test_agent_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  296. package/backend/tests/unit/agent/test_agent_public_api.py +0 -837
  297. package/backend/tests/unit/agent/test_agent_runtime.py +0 -2942
  298. package/backend/tests/unit/channels/__pycache__/test_telegram_channel.cpython-313-pytest-9.0.3.pyc +0 -0
  299. package/backend/tests/unit/channels/test_telegram_channel.py +0 -552
  300. package/backend/tests/unit/logging/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
  301. package/backend/tests/unit/logging/test_logging.py +0 -132
  302. package/backend/tests/unit/prompts/__pycache__/test_prompts.cpython-313-pytest-9.0.3.pyc +0 -0
  303. package/backend/tests/unit/prompts/test_prompts.py +0 -570
  304. package/backend/tests/unit/providers/__pycache__/test_anthropic_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  305. package/backend/tests/unit/providers/__pycache__/test_errors.cpython-313-pytest-9.0.3.pyc +0 -0
  306. package/backend/tests/unit/providers/__pycache__/test_extract_delta_parts.cpython-313-pytest-9.0.3.pyc +0 -0
  307. package/backend/tests/unit/providers/__pycache__/test_openai_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  308. package/backend/tests/unit/providers/__pycache__/test_openai_responses.cpython-313-pytest-9.0.3.pyc +0 -0
  309. package/backend/tests/unit/providers/__pycache__/test_provider_gateway.cpython-313-pytest-9.0.3.pyc +0 -0
  310. package/backend/tests/unit/providers/__pycache__/test_think_tag_parser.cpython-313-pytest-9.0.3.pyc +0 -0
  311. package/backend/tests/unit/providers/test_anthropic_provider.py +0 -185
  312. package/backend/tests/unit/providers/test_errors.py +0 -68
  313. package/backend/tests/unit/providers/test_extract_delta_parts.py +0 -22
  314. package/backend/tests/unit/providers/test_openai_provider.py +0 -139
  315. package/backend/tests/unit/providers/test_openai_responses.py +0 -402
  316. package/backend/tests/unit/providers/test_provider_gateway.py +0 -359
  317. package/backend/tests/unit/providers/test_think_tag_parser.py +0 -36
  318. package/backend/tests/unit/routes/__pycache__/test_prompts_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  319. package/backend/tests/unit/routes/__pycache__/test_providers_route.cpython-313-pytest-9.0.3.pyc +0 -0
  320. package/backend/tests/unit/routes/__pycache__/test_roles_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  321. package/backend/tests/unit/routes/__pycache__/test_settings_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  322. package/backend/tests/unit/routes/test_prompts_routes.py +0 -104
  323. package/backend/tests/unit/routes/test_providers_route.py +0 -370
  324. package/backend/tests/unit/routes/test_roles_routes.py +0 -535
  325. package/backend/tests/unit/routes/test_settings_routes.py +0 -1142
  326. package/backend/tests/unit/runtime/__pycache__/test_bootstrap_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  327. package/backend/tests/unit/runtime/test_bootstrap_runtime.py +0 -1002
  328. package/backend/tests/unit/sandbox/__pycache__/test_sandbox_tools.cpython-313-pytest-9.0.3.pyc +0 -0
  329. package/backend/tests/unit/sandbox/test_sandbox_tools.py +0 -78
  330. package/backend/tests/unit/security/__pycache__/test_security.cpython-313-pytest-9.0.3.pyc +0 -0
  331. package/backend/tests/unit/security/test_security.py +0 -124
  332. package/backend/tests/unit/settings/__pycache__/test_settings_roles.cpython-313-pytest-9.0.3.pyc +0 -0
  333. package/backend/tests/unit/settings/test_settings_roles.py +0 -751
  334. package/backend/tests/unit/test_access.py +0 -45
  335. package/backend/tests/unit/test_cli.py +0 -124
  336. package/backend/tests/unit/test_graph_runtime.py +0 -72
  337. package/backend/tests/unit/test_network.py +0 -51
  338. package/backend/tests/unit/test_state_sqlite_storage.py +0 -159
  339. package/backend/tests/unit/test_workspace_store.py +0 -231
  340. package/backend/tests/unit/tools/__pycache__/test_connect_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  341. package/backend/tests/unit/tools/__pycache__/test_create_agent_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  342. package/backend/tests/unit/tools/__pycache__/test_delete_tab_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  343. package/backend/tests/unit/tools/__pycache__/test_edit_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  344. package/backend/tests/unit/tools/__pycache__/test_exec_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  345. package/backend/tests/unit/tools/__pycache__/test_fetch_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  346. package/backend/tests/unit/tools/__pycache__/test_manage_prompts_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  347. package/backend/tests/unit/tools/__pycache__/test_manage_providers_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  348. package/backend/tests/unit/tools/__pycache__/test_manage_roles_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  349. package/backend/tests/unit/tools/__pycache__/test_manage_settings_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  350. package/backend/tests/unit/tools/__pycache__/test_read_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  351. package/backend/tests/unit/tools/__pycache__/test_set_permissions_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  352. package/backend/tests/unit/tools/__pycache__/test_todo_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  353. package/backend/tests/unit/tools/__pycache__/test_tool_registry.cpython-313-pytest-9.0.3.pyc +0 -0
  354. package/backend/tests/unit/tools/test_connect_tool.py +0 -228
  355. package/backend/tests/unit/tools/test_create_agent_tool.py +0 -436
  356. package/backend/tests/unit/tools/test_delete_tab_tool.py +0 -116
  357. package/backend/tests/unit/tools/test_edit_tool.py +0 -115
  358. package/backend/tests/unit/tools/test_exec_tool.py +0 -81
  359. package/backend/tests/unit/tools/test_fetch_tool.py +0 -65
  360. package/backend/tests/unit/tools/test_manage_prompts_tool.py +0 -117
  361. package/backend/tests/unit/tools/test_manage_providers_tool.py +0 -460
  362. package/backend/tests/unit/tools/test_manage_roles_tool.py +0 -411
  363. package/backend/tests/unit/tools/test_manage_settings_tool.py +0 -611
  364. package/backend/tests/unit/tools/test_read_tool.py +0 -33
  365. package/backend/tests/unit/tools/test_set_permissions_tool.py +0 -595
  366. package/backend/tests/unit/tools/test_todo_tool.py +0 -37
  367. package/backend/tests/unit/tools/test_tool_registry.py +0 -194
  368. package/dist/frontend/assets/AssistantPage-VBohhz4d.js +0 -1
  369. package/dist/frontend/assets/ChannelsPage-CIydPZA_.js +0 -1
  370. package/dist/frontend/assets/McpPage-CHPm2TPY.js +0 -7
  371. package/dist/frontend/assets/PageScaffold-DteOA8V7.js +0 -1
  372. package/dist/frontend/assets/PromptsPage-CSmJ3sZg.js +0 -1
  373. package/dist/frontend/assets/ProvidersPage-sl2jeG4e.js +0 -3
  374. package/dist/frontend/assets/RolesPage-DCe7W6Km.js +0 -1
  375. package/dist/frontend/assets/SettingsPage-Bix9e63E.js +0 -3
  376. package/dist/frontend/assets/ToolsPage-favNkj5C.js +0 -1
  377. package/dist/frontend/assets/WorkspaceCommandDialog-DRS6wiD6.js +0 -1
  378. package/dist/frontend/assets/WorkspacePage-KuaDjt_D.js +0 -3
  379. package/dist/frontend/assets/WorkspacePanels-BZxBw8M5.js +0 -1
  380. package/dist/frontend/assets/alert-dialog-DIBUCmqM.js +0 -1
  381. package/dist/frontend/assets/datetime-eJqd0V2S.js +0 -1
  382. package/dist/frontend/assets/dialog-BOvHIBrg.js +0 -1
  383. package/dist/frontend/assets/elk-worker.min-C9JGDOE-.js +0 -6312
  384. package/dist/frontend/assets/graph-vendor-CHpVij2M.css +0 -1
  385. package/dist/frontend/assets/graph-vendor-DRq_-6fV.js +0 -7
  386. package/dist/frontend/assets/index-Biio-CoI.js +0 -10
  387. package/dist/frontend/assets/index-CmQvO7sl.css +0 -1
  388. package/dist/frontend/assets/layout.worker-jMHqAFbP.js +0 -24
  389. package/dist/frontend/assets/markdown-vendor-C9RtvaJh.js +0 -29
  390. package/dist/frontend/assets/modelParams-DcEhGnu0.js +0 -1
  391. package/dist/frontend/assets/react-vendor-mEs_JJxa.js +0 -9
  392. package/dist/frontend/assets/roles-BbIEIMeG.js +0 -1
  393. package/dist/frontend/assets/rolldown-runtime-BYbx6iT9.js +0 -1
  394. package/dist/frontend/assets/select-D9SwnlXF.js +0 -1
  395. package/dist/frontend/assets/surface-Bzr1FRG4.js +0 -1
  396. package/dist/frontend/assets/triState-DgLlKdRR.js +0 -1
  397. package/dist/frontend/assets/ui-vendor-UazN8rcv.js +0 -51
  398. package/dist/frontend/favicon.svg +0 -4
@@ -1,182 +0,0 @@
1
- import pytest
2
- from fastapi.testclient import TestClient
3
- from starlette.websockets import WebSocketDisconnect
4
-
5
- from flowent.main import create_app
6
-
7
-
8
- def _create_client(monkeypatch, tmp_path, *, configured: bool) -> TestClient:
9
- import flowent.settings as settings_module
10
- from flowent.access import set_access_code
11
-
12
- settings_file = tmp_path / "settings.json"
13
- monkeypatch.setattr(settings_module, "_SETTINGS_FILE", settings_file)
14
- monkeypatch.setattr(settings_module, "_cached_settings", None)
15
-
16
- settings = settings_module.Settings()
17
- if configured:
18
- set_access_code(settings, "TEST-ACCESS-CODE")
19
- settings_module.save_settings(settings)
20
- monkeypatch.setattr(settings_module, "_cached_settings", None)
21
-
22
- return TestClient(create_app())
23
-
24
-
25
- def test_protected_api_requires_admin_session(monkeypatch, tmp_path):
26
- with _create_client(monkeypatch, tmp_path, configured=True) as client:
27
- access_state = client.get("/api/access/state")
28
- protected_response = client.get("/api/settings/bootstrap")
29
-
30
- assert access_state.status_code == 200
31
- assert access_state.json() == {
32
- "authenticated": False,
33
- "configured": True,
34
- "bootstrap_generated": False,
35
- "requires_restart": False,
36
- }
37
- assert protected_response.status_code == 401
38
- assert protected_response.json() == {"detail": "Access denied"}
39
-
40
-
41
- def test_access_login_and_logout_flow(monkeypatch, tmp_path):
42
- with _create_client(monkeypatch, tmp_path, configured=True) as client:
43
- login_response = client.post(
44
- "/api/access/login",
45
- json={"code": "TEST-ACCESS-CODE"},
46
- )
47
- protected_response = client.get("/api/settings/bootstrap")
48
- logout_response = client.post("/api/access/logout")
49
- denied_response = client.get("/api/settings/bootstrap")
50
-
51
- assert login_response.status_code == 200
52
- assert login_response.json()["authenticated"] is True
53
- assert protected_response.status_code == 200
54
- assert logout_response.status_code == 200
55
- assert logout_response.json()["authenticated"] is False
56
- assert denied_response.status_code == 401
57
-
58
-
59
- def test_admin_session_survives_backend_restart(monkeypatch, tmp_path):
60
- import flowent.settings as settings_module
61
-
62
- with _create_client(monkeypatch, tmp_path, configured=True) as client:
63
- login_response = client.post(
64
- "/api/access/login",
65
- json={"code": "TEST-ACCESS-CODE"},
66
- )
67
- session_cookie = client.cookies.get("flowent_admin_session")
68
-
69
- assert login_response.status_code == 200
70
- assert session_cookie
71
- persisted_settings, _ = settings_module._read_settings_file()
72
- assert persisted_settings.access.code == "TEST-ACCESS-CODE"
73
- assert persisted_settings.access.session_signing_secret
74
-
75
- with _create_client(monkeypatch, tmp_path, configured=True) as restarted_client:
76
- restarted_client.cookies.set("flowent_admin_session", session_cookie)
77
- access_state = restarted_client.get("/api/access/state")
78
- protected_response = restarted_client.get("/api/settings/bootstrap")
79
-
80
- assert access_state.status_code == 200
81
- assert access_state.json()["authenticated"] is True
82
- assert protected_response.status_code == 200
83
-
84
-
85
- def test_access_code_rotation_invalidates_existing_admin_session(
86
- monkeypatch,
87
- tmp_path,
88
- ):
89
- import flowent.settings as settings_module
90
- from flowent.access import set_access_code
91
-
92
- with _create_client(monkeypatch, tmp_path, configured=True) as client:
93
- login_response = client.post(
94
- "/api/access/login",
95
- json={"code": "TEST-ACCESS-CODE"},
96
- )
97
- session_cookie = client.cookies.get("flowent_admin_session")
98
-
99
- assert login_response.status_code == 200
100
- assert session_cookie
101
-
102
- rotated_settings = settings_module.get_settings()
103
- set_access_code(rotated_settings, "NEW-ACCESS-CODE")
104
- settings_module.save_settings(rotated_settings)
105
- monkeypatch.setattr(settings_module, "_cached_settings", None)
106
-
107
- with TestClient(create_app()) as restarted_client:
108
- restarted_client.cookies.set("flowent_admin_session", session_cookie)
109
- access_state = restarted_client.get("/api/access/state")
110
- protected_response = restarted_client.get("/api/settings/bootstrap")
111
-
112
- assert access_state.status_code == 200
113
- assert access_state.json()["authenticated"] is False
114
- assert protected_response.status_code == 401
115
- assert protected_response.json() == {"detail": "Access denied"}
116
-
117
-
118
- def test_websocket_requires_admin_session(monkeypatch, tmp_path):
119
- with _create_client(monkeypatch, tmp_path, configured=True) as client:
120
- with pytest.raises(WebSocketDisconnect), client.websocket_connect("/ws/events"):
121
- pass
122
-
123
- login_response = client.post(
124
- "/api/access/login",
125
- json={"code": "TEST-ACCESS-CODE"},
126
- )
127
-
128
- with client.websocket_connect("/ws/events"):
129
- pass
130
-
131
- assert login_response.status_code == 200
132
-
133
-
134
- def test_access_state_reports_bootstrap_generated_code(monkeypatch, tmp_path):
135
- with _create_client(monkeypatch, tmp_path, configured=False) as client:
136
- access_state = client.get("/api/access/state")
137
-
138
- assert access_state.status_code == 200
139
- assert access_state.json() == {
140
- "authenticated": False,
141
- "configured": True,
142
- "bootstrap_generated": True,
143
- "requires_restart": False,
144
- }
145
-
146
-
147
- def test_legacy_hashed_only_access_rotates_to_persisted_code_at_startup(
148
- monkeypatch,
149
- tmp_path,
150
- ):
151
- import flowent.settings as settings_module
152
- from flowent.access import set_access_code, verify_access_code
153
-
154
- settings_file = tmp_path / "settings.json"
155
- monkeypatch.setattr(settings_module, "_SETTINGS_FILE", settings_file)
156
- monkeypatch.setattr(settings_module, "_cached_settings", None)
157
-
158
- settings = settings_module.Settings()
159
- set_access_code(settings, "OLD-ACCESS-CODE")
160
- settings.access.code = ""
161
- settings_module.save_settings(settings)
162
- monkeypatch.setattr(settings_module, "_cached_settings", None)
163
-
164
- with TestClient(create_app()) as client:
165
- access_state = client.get("/api/access/state")
166
-
167
- assert access_state.status_code == 200
168
- assert access_state.json() == {
169
- "authenticated": False,
170
- "configured": True,
171
- "bootstrap_generated": True,
172
- "requires_restart": False,
173
- }
174
-
175
- persisted_settings = settings_module.load_settings()
176
- assert persisted_settings.access.code
177
- assert persisted_settings.access.code != "OLD-ACCESS-CODE"
178
- assert verify_access_code(
179
- persisted_settings.access,
180
- persisted_settings.access.code,
181
- )
182
- assert not verify_access_code(persisted_settings.access, "OLD-ACCESS-CODE")
@@ -1,354 +0,0 @@
1
- import base64
2
- from uuid import UUID
3
-
4
- from flowent.models import (
5
- AssistantText,
6
- ImagePart,
7
- LLMResponse,
8
- ReceivedMessage,
9
- TextPart,
10
- )
11
- from flowent.registry import registry
12
-
13
- _ONE_PIXEL_PNG = base64.b64decode(
14
- "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+aF9sAAAAASUVORK5CYII="
15
- )
16
-
17
-
18
- def _get_assistant_id(client) -> str:
19
- response = client.get("/api/assistant")
20
-
21
- assert response.status_code == 200
22
- assistant_id = response.json()["id"]
23
- UUID(assistant_id)
24
- return assistant_id
25
-
26
-
27
- def test_help_command_returns_visible_command_feedback(client):
28
- assistant_id = _get_assistant_id(client)
29
-
30
- response = client.post("/api/assistant/message", json={"content": "/help"})
31
-
32
- assert response.status_code == 200
33
- assert response.json() == {
34
- "status": "command_executed",
35
- "command_name": "/help",
36
- }
37
-
38
- detail = client.get(f"/api/nodes/{assistant_id}").json()
39
-
40
- assert any(
41
- entry["type"] == "CommandResultEntry"
42
- and entry["command_name"] == "/help"
43
- and "/compact" in entry["content"]
44
- for entry in detail["history"]
45
- )
46
- assert not any(
47
- entry["type"] == "ReceivedMessage" and entry.get("content") == "/help"
48
- for entry in detail["history"]
49
- )
50
-
51
-
52
- def test_clear_command_clears_history_back_to_empty_state(client):
53
- assistant_id = _get_assistant_id(client)
54
- assistant = registry.get(assistant_id)
55
- assert assistant is not None
56
- assistant.history.append(ReceivedMessage(content="Old message", from_id="human"))
57
- assistant.history.append(AssistantText(content="Old reply"))
58
-
59
- response = client.post("/api/assistant/message", json={"content": "/clear"})
60
-
61
- assert response.status_code == 200
62
- assert response.json() == {
63
- "status": "command_executed",
64
- "command_name": "/clear",
65
- }
66
-
67
- detail = client.get(f"/api/nodes/{assistant_id}").json()
68
-
69
- assert not any(
70
- entry["type"] in {"ReceivedMessage", "AssistantText"}
71
- and entry.get("content") in {"Old message", "Old reply"}
72
- for entry in detail["history"]
73
- )
74
- assert not any(
75
- entry["type"] == "CommandResultEntry" and entry["command_name"] == "/clear"
76
- for entry in detail["history"]
77
- )
78
-
79
-
80
- def test_compact_command_replaces_history_with_summary(monkeypatch, client):
81
- assistant_id = _get_assistant_id(client)
82
- assistant = registry.get(assistant_id)
83
- assert assistant is not None
84
- assistant.history.extend(
85
- [
86
- ReceivedMessage(content="Need a concise recap", from_id="human"),
87
- AssistantText(content="I will summarize the open work."),
88
- ]
89
- )
90
-
91
- monkeypatch.setattr(
92
- "flowent.agent.gateway.chat",
93
- lambda *args, **kwargs: LLMResponse(
94
- content=(
95
- "## Current Goal\nShip the slash commands.\n\n"
96
- "## Active Task Boundary\nKeep the fix limited to Assistant chat.\n\n"
97
- "## Key Constraints\nDo not lose persisted context.\n\n"
98
- "## Confirmed Decisions\nUse built-in commands only.\n\n"
99
- "## Open Questions\nNone.\n\n"
100
- "## Next Actions\nWire the UI and tests."
101
- )
102
- ),
103
- )
104
-
105
- response = client.post(
106
- "/api/assistant/message",
107
- json={"content": "/compact slash command rollout"},
108
- )
109
-
110
- assert response.status_code == 200
111
- assert response.json() == {
112
- "status": "command_executed",
113
- "command_name": "/compact",
114
- }
115
-
116
- detail = client.get(f"/api/nodes/{assistant_id}").json()
117
-
118
- assert any(
119
- entry["type"] == "ReceivedMessage"
120
- and entry.get("content") == "Need a concise recap"
121
- for entry in detail["history"]
122
- )
123
- assert any(
124
- entry["type"] == "AssistantText"
125
- and entry.get("content") == "I will summarize the open work."
126
- for entry in detail["history"]
127
- )
128
- assert any(
129
- entry["type"] == "CommandResultEntry"
130
- and entry["command_name"] == "/compact"
131
- and entry.get("include_in_context") is False
132
- and "Compacted this chat for future replies." in entry["content"]
133
- and "Focus: slash command rollout" in entry["content"]
134
- and "Ship the slash commands." not in entry["content"]
135
- for entry in detail["history"]
136
- )
137
-
138
-
139
- def test_retry_message_rewrites_tail_and_reuses_image_parts(monkeypatch, client):
140
- assistant_id = _get_assistant_id(client)
141
- assistant = registry.get(assistant_id)
142
- assert assistant is not None
143
- queued_messages = []
144
-
145
- upload_response = client.post(
146
- "/api/image-assets",
147
- files={"file": ("pixel.png", _ONE_PIXEL_PNG, "image/png")},
148
- )
149
- assert upload_response.status_code == 200
150
- asset_id = upload_response.json()["id"]
151
-
152
- assistant.history.extend(
153
- [
154
- ReceivedMessage(
155
- content="Keep this turn",
156
- from_id="human",
157
- message_id="msg-1",
158
- ),
159
- AssistantText(content="Keep this reply"),
160
- ReceivedMessage(
161
- from_id="human",
162
- parts=[
163
- TextPart(text="Retry this image request"),
164
- ImagePart(
165
- asset_id=asset_id,
166
- mime_type="image/png",
167
- width=1,
168
- height=1,
169
- ),
170
- ],
171
- message_id="msg-2",
172
- ),
173
- AssistantText(content="Discard this reply"),
174
- ]
175
- )
176
-
177
- monkeypatch.setattr(assistant, "supports_input_image", lambda: True)
178
- monkeypatch.setattr(
179
- assistant,
180
- "enqueue_message",
181
- lambda message: queued_messages.append(message),
182
- )
183
-
184
- response = client.post("/api/assistant/messages/msg-2/retry")
185
-
186
- assert response.status_code == 200
187
- payload = response.json()
188
- assert payload["status"] == "retried"
189
- assert payload["message_id"] != "msg-2"
190
-
191
- detail = client.get(f"/api/nodes/{assistant_id}").json()
192
-
193
- assert any(
194
- entry["type"] == "ReceivedMessage"
195
- and entry.get("message_id") == "msg-1"
196
- and entry.get("content") == "Keep this turn"
197
- for entry in detail["history"]
198
- )
199
- assert not any(
200
- entry["type"] == "ReceivedMessage" and entry.get("message_id") == "msg-2"
201
- for entry in detail["history"]
202
- )
203
- assert not any(
204
- entry["type"] == "AssistantText"
205
- and entry.get("content") == "Discard this reply"
206
- for entry in detail["history"]
207
- )
208
- assert any(
209
- entry["type"] == "ReceivedMessage"
210
- and entry.get("message_id") == payload["message_id"]
211
- and entry.get("content") == "Retry this image request[image]"
212
- for entry in detail["history"]
213
- )
214
- assert len(queued_messages) == 1
215
- assert queued_messages[0].message_id == payload["message_id"]
216
- assert queued_messages[0].parts[0].text == "Retry this image request"
217
- assert queued_messages[0].parts[1].asset_id == asset_id
218
-
219
-
220
- def test_retry_message_returns_404_for_missing_anchor(client):
221
- response = client.post("/api/assistant/messages/missing/retry")
222
-
223
- assert response.status_code == 404
224
- assert "was not found" in response.json()["detail"]
225
-
226
-
227
- def test_retry_message_returns_409_when_image_input_is_unsupported(monkeypatch, client):
228
- assistant_id = _get_assistant_id(client)
229
- assistant = registry.get(assistant_id)
230
- assert assistant is not None
231
-
232
- upload_response = client.post(
233
- "/api/image-assets",
234
- files={"file": ("pixel.png", _ONE_PIXEL_PNG, "image/png")},
235
- )
236
- assert upload_response.status_code == 200
237
- asset_id = upload_response.json()["id"]
238
-
239
- assistant.history.append(
240
- ReceivedMessage(
241
- from_id="human",
242
- parts=[
243
- ImagePart(asset_id=asset_id, mime_type="image/png", width=1, height=1)
244
- ],
245
- message_id="msg-image",
246
- )
247
- )
248
- monkeypatch.setattr(assistant, "supports_input_image", lambda: False)
249
-
250
- response = client.post("/api/assistant/messages/msg-image/retry")
251
-
252
- assert response.status_code == 409
253
- assert response.json()["detail"] == (
254
- "Assistant current model does not support `input_image`."
255
- )
256
-
257
-
258
- def test_upload_image_asset_returns_metadata_and_serves_bytes(client):
259
- response = client.post(
260
- "/api/image-assets",
261
- files={"file": ("pixel.png", _ONE_PIXEL_PNG, "image/png")},
262
- )
263
-
264
- assert response.status_code == 200
265
- payload = response.json()
266
- assert payload["mime_type"] == "image/png"
267
- assert payload["width"] == 1
268
- assert payload["height"] == 1
269
-
270
- image_response = client.get(f"/api/image-assets/{payload['id']}")
271
-
272
- assert image_response.status_code == 200
273
- assert image_response.headers["content-type"].startswith("image/png")
274
- assert image_response.content == _ONE_PIXEL_PNG
275
-
276
-
277
- def test_image_message_bypasses_assistant_commands(monkeypatch, client):
278
- assistant_id = _get_assistant_id(client)
279
- assistant = registry.get(assistant_id)
280
- assert assistant is not None
281
- queued_messages = []
282
-
283
- upload_response = client.post(
284
- "/api/image-assets",
285
- files={"file": ("pixel.png", _ONE_PIXEL_PNG, "image/png")},
286
- )
287
- assert upload_response.status_code == 200
288
- asset_id = upload_response.json()["id"]
289
-
290
- monkeypatch.setattr(assistant, "supports_input_image", lambda: True)
291
- monkeypatch.setattr(
292
- assistant, "enqueue_message", lambda message: queued_messages.append(message)
293
- )
294
-
295
- response = client.post(
296
- "/api/assistant/message",
297
- json={
298
- "parts": [
299
- {"type": "text", "text": "/help"},
300
- {
301
- "type": "image",
302
- "asset_id": asset_id,
303
- "mime_type": "image/png",
304
- "width": 1,
305
- "height": 1,
306
- "alt": "Pixel",
307
- },
308
- ]
309
- },
310
- )
311
-
312
- assert response.status_code == 200
313
- assert response.json()["status"] == "sent"
314
- assert len(queued_messages) == 1
315
- assert queued_messages[0].parts[0].text == "/help"
316
- assert queued_messages[0].parts[1].asset_id == asset_id
317
-
318
-
319
- def test_image_message_is_rejected_when_assistant_lacks_input_image_support(
320
- monkeypatch, client
321
- ):
322
- assistant_id = _get_assistant_id(client)
323
- assistant = registry.get(assistant_id)
324
- assert assistant is not None
325
-
326
- upload_response = client.post(
327
- "/api/image-assets",
328
- files={"file": ("pixel.png", _ONE_PIXEL_PNG, "image/png")},
329
- )
330
- assert upload_response.status_code == 200
331
- asset_id = upload_response.json()["id"]
332
-
333
- monkeypatch.setattr(assistant, "supports_input_image", lambda: False)
334
-
335
- response = client.post(
336
- "/api/assistant/message",
337
- json={
338
- "parts": [
339
- {
340
- "type": "image",
341
- "asset_id": asset_id,
342
- "mime_type": "image/png",
343
- "width": 1,
344
- "height": 1,
345
- }
346
- ]
347
- },
348
- )
349
-
350
- assert response.status_code == 409
351
- assert (
352
- response.json()["detail"]
353
- == "Assistant current model does not support `input_image`."
354
- )
@@ -1,61 +0,0 @@
1
- import json
2
-
3
- from fastapi.testclient import TestClient
4
-
5
- from flowent.main import create_app
6
-
7
-
8
- def _create_client(monkeypatch, tmp_path, *, serve_frontend: bool) -> TestClient:
9
- import flowent.settings as settings_module
10
-
11
- settings_file = tmp_path / "settings.json"
12
- static_dir = tmp_path / "static"
13
- assets_dir = static_dir / "assets"
14
-
15
- assets_dir.mkdir(parents=True)
16
- (static_dir / "index.html").write_text(
17
- "<!doctype html><html><body>Flowent</body></html>",
18
- encoding="utf-8",
19
- )
20
- (assets_dir / "app.js").write_text("console.log('flowent')", encoding="utf-8")
21
- settings_file.write_text(
22
- json.dumps(
23
- {
24
- "event_log": {"timestamp_format": "absolute"},
25
- "model": {"active_provider_id": "", "active_model": ""},
26
- "custom_prompt": "",
27
- "custom_post_prompt": "",
28
- "providers": [],
29
- "roles": [],
30
- }
31
- ),
32
- encoding="utf-8",
33
- )
34
-
35
- monkeypatch.setattr(settings_module, "_SETTINGS_FILE", settings_file)
36
- monkeypatch.setattr(settings_module, "_cached_settings", None)
37
- monkeypatch.setenv("FLOWENT_STATIC_DIR", str(static_dir))
38
-
39
- return TestClient(create_app(serve_frontend=serve_frontend))
40
-
41
-
42
- def test_production_app_serves_frontend_index(monkeypatch, tmp_path):
43
- with _create_client(monkeypatch, tmp_path, serve_frontend=True) as client:
44
- response = client.get("/")
45
- nested_response = client.get("/workspace")
46
- asset_response = client.get("/assets/app.js")
47
-
48
- assert response.status_code == 200
49
- assert response.headers["content-type"].startswith("text/html")
50
- assert nested_response.status_code == 200
51
- assert nested_response.headers["content-type"].startswith("text/html")
52
- assert asset_response.status_code == 200
53
-
54
-
55
- def test_dev_app_does_not_serve_frontend_routes(monkeypatch, tmp_path):
56
- with _create_client(monkeypatch, tmp_path, serve_frontend=False) as client:
57
- root_response = client.get("/")
58
- page_response = client.get("/workspace")
59
-
60
- assert root_response.status_code == 404
61
- assert page_response.status_code == 404