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
@@ -3,23 +3,17 @@ from __future__ import annotations
3
3
  import argparse
4
4
  import os
5
5
  import sys
6
-
7
- APP_DATA_DIR_ENV_VAR = "FLOWENT_APP_DATA_DIR"
6
+ from pathlib import Path
8
7
 
9
8
 
10
9
  def main(argv: list[str] | None = None) -> None:
11
10
  parser = argparse.ArgumentParser(
12
11
  prog="flowent",
13
- description="Flowent — multi-agent collaboration framework",
12
+ description="Flowent",
14
13
  )
15
14
  subparsers = parser.add_subparsers(dest="command")
16
- access_parser = subparsers.add_parser("access", help="Flowent access commands")
17
- access_subparsers = access_parser.add_subparsers(dest="access_command")
18
- access_subparsers.add_parser(
19
- "refresh",
20
- help="Generate and persist a new admin access code",
21
- )
22
- access_subparsers.add_parser("reset", help="Clear the persisted admin access code")
15
+ apply_patch_parser = subparsers.add_parser("apply-patch", help=argparse.SUPPRESS)
16
+ apply_patch_parser.add_argument("--cwd", required=True)
23
17
  parser.add_argument(
24
18
  "--host",
25
19
  "--hostname",
@@ -42,24 +36,16 @@ def main(argv: list[str] | None = None) -> None:
42
36
  parser.add_argument(
43
37
  "--app-data-dir",
44
38
  default="",
45
- help="Override the Flowent app data directory for this process",
39
+ help=argparse.SUPPRESS,
46
40
  )
47
41
  args = parser.parse_args(argv)
48
42
 
49
- if isinstance(args.app_data_dir, str) and args.app_data_dir.strip():
50
- os.environ[APP_DATA_DIR_ENV_VAR] = args.app_data_dir.strip()
51
-
52
- if args.command == "access" and args.access_command == "refresh":
53
- from flowent.access import refresh_local_access
54
-
55
- print(refresh_local_access())
56
- return
57
-
58
- if args.command == "access" and args.access_command == "reset":
59
- from flowent.access import reset_local_access
43
+ if args.command == "apply-patch":
44
+ from flowent.patch import run_apply_patch_cli
60
45
 
61
- print(reset_local_access())
62
- return
46
+ raise SystemExit(
47
+ run_apply_patch_cli(cwd=Path(args.cwd), patch=sys.stdin.read())
48
+ )
63
49
 
64
50
  if args.version:
65
51
  try:
@@ -72,6 +58,15 @@ def main(argv: list[str] | None = None) -> None:
72
58
  print(f"flowent {ver}")
73
59
  sys.exit(0)
74
60
 
61
+ from flowent.logging import configure_logging
62
+
63
+ configure_logging()
64
+
65
+ import logging
66
+
67
+ logger = logging.getLogger("flowent.cli")
68
+ logger.info("Starting Flowent on %s:%s", args.host, args.port)
69
+
75
70
  import uvicorn
76
71
 
77
72
  uvicorn.run(
@@ -0,0 +1,127 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from pathlib import Path
5
+
6
+ from flowent.llm import ChatMessage
7
+ from flowent.tools import tool_specs
8
+
9
+ DEFAULT_PROJECT_INSTRUCTIONS_MAX_BYTES = 32768
10
+ PROJECT_DOC_FILENAME = "AGENTS.md"
11
+ PROJECT_DOC_OVERRIDE_FILENAME = "AGENTS.override.md"
12
+
13
+
14
+ def project_instructions_max_bytes() -> int:
15
+ raw_limit = os.environ.get("FLOWENT_PROJECT_INSTRUCTIONS_MAX_BYTES", "")
16
+ try:
17
+ return max(0, int(raw_limit))
18
+ except ValueError:
19
+ return DEFAULT_PROJECT_INSTRUCTIONS_MAX_BYTES
20
+
21
+
22
+ def project_root(cwd: Path) -> Path:
23
+ resolved_cwd = cwd.resolve(strict=False)
24
+ for ancestor in (resolved_cwd, *resolved_cwd.parents):
25
+ if (ancestor / ".git").exists():
26
+ return ancestor
27
+ return resolved_cwd
28
+
29
+
30
+ def project_doc_path(directory: Path) -> Path | None:
31
+ override = directory / PROJECT_DOC_OVERRIDE_FILENAME
32
+ if override.is_file() or override.is_symlink():
33
+ return override
34
+ project_doc = directory / PROJECT_DOC_FILENAME
35
+ if project_doc.is_file() or project_doc.is_symlink():
36
+ return project_doc
37
+ return None
38
+
39
+
40
+ def project_doc_paths(cwd: Path) -> list[Path]:
41
+ root = project_root(cwd)
42
+ resolved_cwd = cwd.resolve(strict=False)
43
+ directories: list[Path] = []
44
+ current = resolved_cwd
45
+ while True:
46
+ directories.append(current)
47
+ if current == root:
48
+ break
49
+ parent = current.parent
50
+ if parent == current:
51
+ break
52
+ current = parent
53
+
54
+ return [
55
+ doc_path
56
+ for directory in reversed(directories)
57
+ if (doc_path := project_doc_path(directory)) is not None
58
+ ]
59
+
60
+
61
+ def read_project_instructions(cwd: Path) -> str:
62
+ remaining_bytes = project_instructions_max_bytes()
63
+ if remaining_bytes <= 0:
64
+ return ""
65
+
66
+ sections: list[str] = []
67
+ for path in project_doc_paths(cwd):
68
+ if remaining_bytes <= 0:
69
+ break
70
+ try:
71
+ content = path.read_bytes()[:remaining_bytes]
72
+ except OSError:
73
+ continue
74
+ text = content.decode(errors="replace")
75
+ if text.strip():
76
+ sections.append(text)
77
+ remaining_bytes -= len(content)
78
+
79
+ return "\n\n".join(sections)
80
+
81
+
82
+ def project_instructions_message(cwd: Path) -> ChatMessage | None:
83
+ instructions = read_project_instructions(cwd)
84
+ if not instructions:
85
+ return None
86
+ return ChatMessage(
87
+ role="user",
88
+ content=(
89
+ f"# AGENTS.md instructions for {cwd.resolve(strict=False)}\n\n"
90
+ f"<INSTRUCTIONS>\n{instructions}\n</INSTRUCTIONS>"
91
+ ),
92
+ )
93
+
94
+
95
+ def tool_names() -> list[str]:
96
+ names: list[str] = []
97
+ for spec in tool_specs():
98
+ function = spec.get("function")
99
+ if isinstance(function, dict) and isinstance(function.get("name"), str):
100
+ names.append(function["name"])
101
+ return names
102
+
103
+
104
+ def environment_context_message(cwd: Path) -> ChatMessage:
105
+ rendered_tools = "\n".join(f" <tool>{name}</tool>" for name in tool_names())
106
+ return ChatMessage(
107
+ role="user",
108
+ content=(
109
+ "<environment_context>\n"
110
+ f" <cwd>{cwd.resolve(strict=False)}</cwd>\n"
111
+ " <filesystem>workspace-write</filesystem>\n"
112
+ " <network>enabled</network>\n"
113
+ " <tools>\n"
114
+ f"{rendered_tools}\n"
115
+ " </tools>\n"
116
+ "</environment_context>"
117
+ ),
118
+ )
119
+
120
+
121
+ def runtime_context_messages(cwd: Path) -> list[ChatMessage]:
122
+ messages: list[ChatMessage] = []
123
+ project_message = project_instructions_message(cwd)
124
+ if project_message is not None:
125
+ messages.append(project_message)
126
+ messages.append(environment_context_message(cwd))
127
+ return messages
@@ -0,0 +1,256 @@
1
+ import logging
2
+ from collections.abc import AsyncIterator, Awaitable, Mapping, Sequence
3
+ from enum import StrEnum
4
+ from typing import Any, Literal, Protocol
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field
7
+
8
+ from flowent.logging import TRACE_LEVEL, configure_litellm_logging
9
+
10
+
11
+ class ProviderFormat(StrEnum):
12
+ OPENAI = "openai"
13
+ OPENAI_RESPONSES = "openai_responses"
14
+ ANTHROPIC = "anthropic"
15
+ GEMINI = "gemini"
16
+
17
+
18
+ class ProviderConnection(BaseModel):
19
+ model_config = ConfigDict(extra="forbid")
20
+
21
+ name: str = Field(min_length=1)
22
+ provider: ProviderFormat
23
+ model: str = Field(min_length=1)
24
+ secret_reference: str = Field(min_length=1)
25
+ base_url: str | None = None
26
+
27
+
28
+ class ChatMessage(BaseModel):
29
+ model_config = ConfigDict(extra="forbid")
30
+
31
+ role: Literal["system", "user", "assistant"]
32
+ content: str
33
+
34
+
35
+ class ToolCallDelta(BaseModel):
36
+ model_config = ConfigDict(extra="forbid")
37
+
38
+ arguments: str = ""
39
+ id: str = ""
40
+ index: int = 0
41
+ name: str = ""
42
+ type: str = "function"
43
+
44
+
45
+ class CompletionCallable(Protocol):
46
+ def __call__(self, **kwargs: Any) -> Awaitable[Any]: ...
47
+
48
+
49
+ class ModelListCallable(Protocol):
50
+ def __call__(self, **kwargs: Any) -> Sequence[str]: ...
51
+
52
+
53
+ logger = logging.getLogger("flowent.llm")
54
+
55
+
56
+ MODEL_PREFIXES: dict[ProviderFormat, str] = {
57
+ ProviderFormat.OPENAI: "openai",
58
+ ProviderFormat.OPENAI_RESPONSES: "openai",
59
+ ProviderFormat.ANTHROPIC: "anthropic",
60
+ ProviderFormat.GEMINI: "gemini",
61
+ }
62
+
63
+
64
+ def provider_model_name(connection: ProviderConnection) -> str:
65
+ return f"{MODEL_PREFIXES[connection.provider]}/{connection.model}"
66
+
67
+
68
+ def provider_litellm_name(provider: ProviderFormat) -> str:
69
+ return MODEL_PREFIXES[provider]
70
+
71
+
72
+ def normalize_provider_model_name(provider: ProviderFormat, model: str) -> str:
73
+ prefix = f"{provider_litellm_name(provider)}/"
74
+ if model.startswith(prefix):
75
+ return model.removeprefix(prefix)
76
+ return model
77
+
78
+
79
+ def unique_model_names(provider: ProviderFormat, models: Sequence[str]) -> list[str]:
80
+ seen: set[str] = set()
81
+ normalized_models: list[str] = []
82
+ for model in models:
83
+ normalized_model = normalize_provider_model_name(provider, model)
84
+ if normalized_model in seen:
85
+ continue
86
+ seen.add(normalized_model)
87
+ normalized_models.append(normalized_model)
88
+ return normalized_models
89
+
90
+
91
+ def list_provider_models(
92
+ *,
93
+ provider: ProviderFormat,
94
+ secret_reference: str,
95
+ base_url: str | None = None,
96
+ model_lister: ModelListCallable | None = None,
97
+ ) -> list[str]:
98
+ if model_lister is None:
99
+ from litellm import get_valid_models
100
+
101
+ configure_litellm_logging()
102
+ model_lister = get_valid_models
103
+
104
+ models = model_lister(
105
+ api_base=base_url,
106
+ api_key=secret_reference,
107
+ check_provider_endpoint=True,
108
+ custom_llm_provider=provider_litellm_name(provider),
109
+ )
110
+ return unique_model_names(provider, models)
111
+
112
+
113
+ def build_litellm_request(
114
+ connection: ProviderConnection,
115
+ messages: Sequence[ChatMessage | Mapping[str, Any]],
116
+ *,
117
+ stream: bool = False,
118
+ tools: Sequence[Mapping[str, Any]] | None = None,
119
+ ) -> dict[str, Any]:
120
+ request_messages = [
121
+ message.model_dump() if isinstance(message, ChatMessage) else dict(message)
122
+ for message in messages
123
+ ]
124
+ request: dict[str, Any] = {
125
+ "api_key": connection.secret_reference,
126
+ "messages": request_messages,
127
+ "model": provider_model_name(connection),
128
+ }
129
+ if tools:
130
+ request["tools"] = list(tools)
131
+ if stream:
132
+ request["stream"] = True
133
+ if connection.base_url:
134
+ request["api_base"] = connection.base_url
135
+ logger.log(
136
+ TRACE_LEVEL,
137
+ "Built LiteLLM request provider=%s model=%s base_url=%s stream=%s tools=%s messages=%r",
138
+ connection.provider,
139
+ connection.model,
140
+ connection.base_url or "",
141
+ stream,
142
+ bool(tools),
143
+ request_messages,
144
+ )
145
+ return request
146
+
147
+
148
+ async def complete_chat(
149
+ connection: ProviderConnection,
150
+ messages: Sequence[ChatMessage | Mapping[str, Any]],
151
+ *,
152
+ completion: CompletionCallable | None = None,
153
+ tools: Sequence[Mapping[str, Any]] | None = None,
154
+ ) -> ChatMessage:
155
+ if completion is None:
156
+ from litellm import acompletion
157
+
158
+ configure_litellm_logging()
159
+ completion = acompletion
160
+
161
+ logger.debug(
162
+ "Starting LLM completion provider=%s model=%s",
163
+ connection.provider,
164
+ connection.model,
165
+ )
166
+ response = await completion(
167
+ **build_litellm_request(connection, messages, tools=tools)
168
+ )
169
+ logger.log(TRACE_LEVEL, "LLM completion response=%r", response)
170
+ choice = response["choices"][0]["message"]
171
+ return ChatMessage(role=choice.get("role", "assistant"), content=choice["content"])
172
+
173
+
174
+ def value_at(value: Any, key: str, default: Any = None) -> Any:
175
+ if isinstance(value, Mapping):
176
+ return value.get(key, default)
177
+ return getattr(value, key, default)
178
+
179
+
180
+ def chunk_delta_content(chunk: Any) -> str:
181
+ try:
182
+ content = chunk.choices[0].delta.content
183
+ except (AttributeError, IndexError, TypeError):
184
+ try:
185
+ content = chunk["choices"][0]["delta"].get("content")
186
+ except (KeyError, IndexError, TypeError, AttributeError):
187
+ return ""
188
+ return content if isinstance(content, str) else ""
189
+
190
+
191
+ def chunk_delta_tool_calls(chunk: Any) -> list[ToolCallDelta]:
192
+ try:
193
+ choice = chunk.choices[0]
194
+ delta = choice.delta
195
+ except (AttributeError, IndexError, TypeError):
196
+ try:
197
+ delta = chunk["choices"][0]["delta"]
198
+ except (KeyError, IndexError, TypeError):
199
+ return []
200
+
201
+ raw_tool_calls = value_at(delta, "tool_calls", [])
202
+ if not raw_tool_calls:
203
+ return []
204
+
205
+ tool_call_deltas: list[ToolCallDelta] = []
206
+ for position, raw_tool_call in enumerate(raw_tool_calls):
207
+ function = value_at(raw_tool_call, "function", {})
208
+ index = value_at(raw_tool_call, "index", position)
209
+ tool_call_deltas.append(
210
+ ToolCallDelta(
211
+ arguments=value_at(function, "arguments", "") or "",
212
+ id=value_at(raw_tool_call, "id", "") or "",
213
+ index=index if isinstance(index, int) else position,
214
+ name=value_at(function, "name", "") or "",
215
+ type=value_at(raw_tool_call, "type", "function") or "function",
216
+ )
217
+ )
218
+ return tool_call_deltas
219
+
220
+
221
+ async def stream_chat_chunks(
222
+ connection: ProviderConnection,
223
+ messages: Sequence[ChatMessage | Mapping[str, Any]],
224
+ *,
225
+ completion: CompletionCallable | None = None,
226
+ tools: Sequence[Mapping[str, Any]] | None = None,
227
+ ) -> AsyncIterator[Any]:
228
+ if completion is None:
229
+ from litellm import acompletion
230
+
231
+ configure_litellm_logging()
232
+ completion = acompletion
233
+
234
+ logger.debug(
235
+ "Starting streaming LLM completion provider=%s model=%s",
236
+ connection.provider,
237
+ connection.model,
238
+ )
239
+ response = await completion(
240
+ **build_litellm_request(connection, messages, stream=True, tools=tools)
241
+ )
242
+ async for chunk in response:
243
+ logger.log(TRACE_LEVEL, "LLM stream chunk=%r", chunk)
244
+ yield chunk
245
+
246
+
247
+ async def stream_chat(
248
+ connection: ProviderConnection,
249
+ messages: Sequence[ChatMessage | Mapping[str, Any]],
250
+ *,
251
+ completion: CompletionCallable | None = None,
252
+ ) -> AsyncIterator[str]:
253
+ async for chunk in stream_chat_chunks(connection, messages, completion=completion):
254
+ content = chunk_delta_content(chunk)
255
+ if content:
256
+ yield content