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,655 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import base64
4
- import json
5
- import time
6
- from collections.abc import Callable
7
- from typing import Any
8
-
9
- from loguru import logger
10
-
11
- from flowent.model_metadata import build_model_info
12
- from flowent.models import (
13
- LLMOutputImagePart,
14
- LLMOutputTextPart,
15
- LLMResponse,
16
- LLMUsage,
17
- ModelInfo,
18
- )
19
- from flowent.models import ToolCallResult as ToolCall
20
- from flowent.network import (
21
- RequestException,
22
- create_http_session,
23
- iter_response_lines,
24
- read_response_text,
25
- response_looks_like_html,
26
- truncate_text,
27
- )
28
- from flowent.providers import LLMProvider
29
- from flowent.providers.content import (
30
- collapse_parts_to_text,
31
- to_openai_responses_content,
32
- )
33
- from flowent.providers.errors import (
34
- build_access_blocked_error,
35
- build_network_error,
36
- build_status_error,
37
- )
38
- from flowent.providers.headers import merge_headers
39
- from flowent.settings import ModelParams
40
-
41
- REASONING_MODEL_PREFIXES = ("gpt-5", "o1", "o3", "o4")
42
- VERBOSITY_MODEL_PREFIXES = ("gpt-5",)
43
- REASONING_DELTA_EVENT_TYPES = {
44
- "response.reasoning.delta",
45
- "response.reasoning_text.delta",
46
- "response.reasoning_summary_text.delta",
47
- }
48
-
49
-
50
- def _supports_reasoning(model: str) -> bool:
51
- return model.startswith(REASONING_MODEL_PREFIXES)
52
-
53
-
54
- def _build_reasoning_config(
55
- model: str,
56
- model_params: ModelParams | None,
57
- ) -> dict[str, str] | None:
58
- if not _supports_reasoning(model) or model_params is None:
59
- return None
60
- effort = model_params.reasoning_effort
61
- if effort is None or effort == "none":
62
- return None
63
-
64
- return {
65
- "effort": effort,
66
- "summary": "detailed",
67
- }
68
-
69
-
70
- def _build_text_config(
71
- model: str,
72
- model_params: ModelParams | None,
73
- ) -> dict[str, object] | None:
74
- if model_params is None or not model.startswith(VERBOSITY_MODEL_PREFIXES):
75
- return None
76
- if not model_params.verbosity:
77
- return None
78
-
79
- return {
80
- "format": {"type": "text"},
81
- "verbosity": model_params.verbosity,
82
- }
83
-
84
-
85
- def _extract_reasoning_text_from_item(item: dict[str, Any]) -> str | None:
86
- parts: list[str] = []
87
-
88
- for key in ("summary", "content"):
89
- raw_value = item.get(key)
90
-
91
- if isinstance(raw_value, str):
92
- if raw_value.strip():
93
- parts.append(raw_value)
94
- continue
95
-
96
- if not isinstance(raw_value, list):
97
- continue
98
-
99
- for entry in raw_value:
100
- if not isinstance(entry, dict):
101
- continue
102
- text = entry.get("text")
103
- if isinstance(text, str) and text.strip():
104
- parts.append(text)
105
-
106
- if not parts:
107
- return None
108
-
109
- return "".join(parts)
110
-
111
-
112
- def _extract_reasoning_text_from_output(output: Any) -> str | None:
113
- if not isinstance(output, list):
114
- return None
115
-
116
- parts: list[str] = []
117
- for item in output:
118
- if not isinstance(item, dict):
119
- continue
120
- if item.get("type") != "reasoning":
121
- continue
122
- text = _extract_reasoning_text_from_item(item)
123
- if text:
124
- parts.append(text)
125
-
126
- if not parts:
127
- return None
128
-
129
- return "\n\n".join(parts)
130
-
131
-
132
- def _extract_reasoning_tokens(response: dict[str, Any]) -> int:
133
- usage = response.get("usage")
134
- if not isinstance(usage, dict):
135
- return 0
136
-
137
- output_tokens_details = usage.get("output_tokens_details")
138
- if not isinstance(output_tokens_details, dict):
139
- return 0
140
-
141
- tokens = output_tokens_details.get("reasoning_tokens")
142
- return tokens if isinstance(tokens, int) and tokens > 0 else 0
143
-
144
-
145
- def _collect_usage_details(
146
- value: dict[str, Any],
147
- *,
148
- prefix: str = "",
149
- ) -> dict[str, int]:
150
- details: dict[str, int] = {}
151
- for key, raw_item in value.items():
152
- detail_key = f"{prefix}.{key}" if prefix else key
153
- if isinstance(raw_item, bool):
154
- continue
155
- if isinstance(raw_item, int) and raw_item >= 0:
156
- details[detail_key] = raw_item
157
- continue
158
- if isinstance(raw_item, dict):
159
- details.update(_collect_usage_details(raw_item, prefix=detail_key))
160
- return details
161
-
162
-
163
- def _extract_usage(response: dict[str, Any]) -> LLMUsage | None:
164
- usage = response.get("usage")
165
- if not isinstance(usage, dict):
166
- return None
167
-
168
- total_tokens = usage.get("total_tokens")
169
- if isinstance(total_tokens, bool) or not isinstance(total_tokens, int):
170
- return None
171
-
172
- input_tokens = usage.get("input_tokens")
173
- if isinstance(input_tokens, bool) or not isinstance(input_tokens, int):
174
- input_tokens = None
175
-
176
- output_tokens = usage.get("output_tokens")
177
- if isinstance(output_tokens, bool) or not isinstance(output_tokens, int):
178
- output_tokens = None
179
-
180
- cached_input_tokens = usage.get("cached_input_tokens")
181
- if isinstance(cached_input_tokens, bool) or not isinstance(
182
- cached_input_tokens, int
183
- ):
184
- cached_input_tokens = None
185
- input_token_details = usage.get("input_tokens_details")
186
- if (
187
- cached_input_tokens is None
188
- and isinstance(input_token_details, dict)
189
- and isinstance(input_token_details.get("cached_tokens"), int)
190
- ):
191
- cached_input_tokens = input_token_details["cached_tokens"]
192
-
193
- details = _collect_usage_details(usage)
194
- for key in (
195
- "total_tokens",
196
- "input_tokens",
197
- "output_tokens",
198
- "cached_input_tokens",
199
- "input_tokens_details.cached_tokens",
200
- ):
201
- details.pop(key, None)
202
-
203
- return LLMUsage(
204
- total_tokens=total_tokens,
205
- input_tokens=input_tokens,
206
- output_tokens=output_tokens,
207
- cached_input_tokens=cached_input_tokens,
208
- cache_read_tokens=cached_input_tokens,
209
- details=details,
210
- )
211
-
212
-
213
- def _decode_output_image(item: dict[str, Any]) -> LLMOutputImagePart | None:
214
- encoded_data = (
215
- item.get("result") or item.get("image_base64") or item.get("b64_json")
216
- )
217
- if not isinstance(encoded_data, str) or not encoded_data:
218
- return None
219
- try:
220
- image_data = base64.b64decode(encoded_data)
221
- except ValueError:
222
- return None
223
- mime_type = item.get("mime_type")
224
- return LLMOutputImagePart(
225
- data=image_data,
226
- mime_type=mime_type if isinstance(mime_type, str) else "image/png",
227
- )
228
-
229
-
230
- def _extract_output_parts(output: Any) -> list[LLMOutputTextPart | LLMOutputImagePart]:
231
- if not isinstance(output, list):
232
- return []
233
-
234
- parts: list[LLMOutputTextPart | LLMOutputImagePart] = []
235
- for item in output:
236
- if not isinstance(item, dict):
237
- continue
238
- item_type = item.get("type")
239
- if item_type == "message":
240
- content = item.get("content")
241
- if not isinstance(content, list):
242
- continue
243
- for entry in content:
244
- if not isinstance(entry, dict):
245
- continue
246
- entry_type = entry.get("type")
247
- text = entry.get("text")
248
- if entry_type in {"output_text", "text"} and isinstance(text, str):
249
- parts.append(LLMOutputTextPart(text=text))
250
- elif item_type == "image_generation_call":
251
- image_part = _decode_output_image(item)
252
- if image_part is not None:
253
- parts.append(image_part)
254
- return parts
255
-
256
-
257
- def _format_reasoning_fallback(reasoning_tokens: int) -> str:
258
- if reasoning_tokens > 0:
259
- return f"Internal reasoning · {reasoning_tokens}"
260
-
261
- return "Internal reasoning"
262
-
263
-
264
- class OpenAIResponsesProvider(LLMProvider):
265
- def __init__(
266
- self,
267
- provider_name: str,
268
- api_base_url: str,
269
- api_key: str = "",
270
- headers: dict[str, str] | None = None,
271
- model: str = "",
272
- request_timeout_seconds: float = 120.0,
273
- ) -> None:
274
- self._provider_name = provider_name
275
- self._api_base_url = api_base_url.rstrip("/")
276
- self._api_key = api_key
277
- self._header_overrides = dict(headers or {})
278
- self._model = model
279
- self._request_timeout_seconds = request_timeout_seconds
280
- self._client = create_http_session(timeout=self._request_timeout_seconds)
281
-
282
- def _headers(self) -> dict[str, str]:
283
- headers: dict[str, str] = {"Content-Type": "application/json"}
284
- if self._api_key:
285
- headers["Authorization"] = f"Bearer {self._api_key}"
286
- return merge_headers(headers, self._header_overrides)
287
-
288
- def _convert_tools(self, tools: list[dict[str, Any]]) -> list[dict[str, Any]]:
289
- result = []
290
- for t in tools:
291
- fn = t.get("function", {})
292
- result.append(
293
- {
294
- "type": "function",
295
- "name": fn.get("name", ""),
296
- "description": fn.get("description", ""),
297
- "parameters": fn.get("parameters", {}),
298
- }
299
- )
300
- return result
301
-
302
- def _convert_messages(
303
- self, messages: list[dict[str, Any]]
304
- ) -> tuple[str | None, list[dict[str, Any]]]:
305
- system_prompt: str | None = None
306
- input_items: list[dict[str, Any]] = []
307
-
308
- for msg in messages:
309
- role = msg.get("role")
310
- content = msg.get("content")
311
-
312
- if role == "system":
313
- system_prompt = content
314
- continue
315
-
316
- if role == "user":
317
- input_items.append(
318
- {
319
- "role": "user",
320
- "content": to_openai_responses_content(
321
- content,
322
- allow_images=True,
323
- ),
324
- }
325
- )
326
- elif role == "assistant":
327
- tool_calls = msg.get("tool_calls")
328
- if tool_calls:
329
- for tc in tool_calls:
330
- fn = tc.get("function", {})
331
- input_items.append(
332
- {
333
- "type": "function_call",
334
- "call_id": tc.get("id", ""),
335
- "name": fn.get("name", ""),
336
- "arguments": fn.get("arguments", "{}"),
337
- }
338
- )
339
- else:
340
- input_items.append(
341
- {
342
- "role": "assistant",
343
- "content": collapse_parts_to_text(content),
344
- }
345
- )
346
- elif role == "tool":
347
- input_items.append(
348
- {
349
- "type": "function_call_output",
350
- "call_id": msg.get("tool_call_id", ""),
351
- "output": content or "",
352
- }
353
- )
354
-
355
- return system_prompt, input_items
356
-
357
- def _supports_output_image(self) -> bool:
358
- model_info = build_model_info(
359
- provider_type="openai_responses",
360
- model_id=self._model,
361
- )
362
- return model_info.capabilities.output_image
363
-
364
- def chat(
365
- self,
366
- messages: list[dict[str, Any]],
367
- tools: list[dict[str, Any]] | None = None,
368
- on_chunk: Callable[[str, str], None] | None = None,
369
- register_interrupt: Callable[[Callable[[], None] | None], None] | None = None,
370
- model_params: ModelParams | None = None,
371
- ) -> LLMResponse:
372
- url = f"{self._api_base_url}/responses"
373
- system_prompt, input_items = self._convert_messages(messages)
374
-
375
- payload: dict[str, Any] = {
376
- "model": self._model,
377
- "input": input_items,
378
- "stream": True,
379
- }
380
- reasoning = _build_reasoning_config(self._model, model_params)
381
- if reasoning:
382
- payload["reasoning"] = reasoning
383
- text_config = _build_text_config(self._model, model_params)
384
- if text_config:
385
- payload["text"] = text_config
386
- if model_params is not None:
387
- if model_params.max_output_tokens is not None:
388
- payload["max_output_tokens"] = model_params.max_output_tokens
389
- if reasoning is None:
390
- if model_params.temperature is not None:
391
- payload["temperature"] = model_params.temperature
392
- if model_params.top_p is not None:
393
- payload["top_p"] = model_params.top_p
394
- if system_prompt:
395
- payload["instructions"] = system_prompt
396
- payload_tools = []
397
- if tools:
398
- payload_tools.extend(self._convert_tools(tools))
399
- if self._supports_output_image():
400
- payload_tools.append({"type": "image_generation"})
401
- if payload_tools:
402
- payload["tools"] = payload_tools
403
- payload["tool_choice"] = "auto"
404
-
405
- logger.debug(
406
- "[{}] OpenAI Responses chat request: model={}, input_items={}, tools={}",
407
- self._provider_name,
408
- self._model,
409
- len(input_items),
410
- len(tools) if tools else 0,
411
- )
412
-
413
- t0 = time.perf_counter()
414
- content_parts: list[str] = []
415
- thinking_parts: list[str] = []
416
- response_parts: list[LLMOutputTextPart | LLMOutputImagePart] | None = None
417
- tool_calls: list[ToolCall] = []
418
- chunk_count = 0
419
- saw_reasoning_item = False
420
- saw_reasoning_text = False
421
- reasoning_tokens = 0
422
- response_usage: LLMUsage | None = None
423
- raw_usage: dict[str, Any] | None = None
424
-
425
- current_tool: dict[str, Any] = {}
426
- client = self._client
427
- if register_interrupt is not None:
428
- register_interrupt(client.close)
429
-
430
- try:
431
- with client.stream(
432
- "POST",
433
- url,
434
- headers=self._headers(),
435
- json=payload,
436
- ) as response:
437
- if register_interrupt is not None:
438
- register_interrupt(response.close)
439
- if response_looks_like_html(response):
440
- raise build_access_blocked_error(
441
- provider_name=self._provider_name,
442
- provider_type="openai_responses",
443
- model=self._model,
444
- base_url=self._api_base_url,
445
- status_code=response.status_code,
446
- detail=truncate_text(read_response_text(response)),
447
- )
448
- if response.status_code != 200:
449
- body = truncate_text(read_response_text(response))
450
- elapsed = time.perf_counter() - t0
451
- logger.error(
452
- "LLM API error [provider={}, model={}, type=openai_responses]: {} - {} ({:.2f}s)",
453
- self._provider_name,
454
- self._model,
455
- response.status_code,
456
- body[:500],
457
- elapsed,
458
- )
459
- raise build_status_error(
460
- provider_name=self._provider_name,
461
- provider_type="openai_responses",
462
- model=self._model,
463
- base_url=self._api_base_url,
464
- status_code=response.status_code,
465
- body=body,
466
- )
467
-
468
- for line in iter_response_lines(response):
469
- if not line or line.startswith(":"):
470
- continue
471
- if not line.startswith("data: "):
472
- continue
473
- data_str = line[6:]
474
- if data_str.strip() == "[DONE]":
475
- break
476
-
477
- try:
478
- event = json.loads(data_str)
479
- except json.JSONDecodeError:
480
- continue
481
-
482
- chunk_count += 1
483
- event_type = event.get("type", "")
484
-
485
- if event_type in REASONING_DELTA_EVENT_TYPES:
486
- text = event.get("delta", "")
487
- if text:
488
- saw_reasoning_text = True
489
- thinking_parts.append(text)
490
- if on_chunk:
491
- on_chunk("thinking", text)
492
-
493
- elif event_type == "response.output_text.delta":
494
- text = event.get("delta", "")
495
- if text:
496
- content_parts.append(text)
497
- if on_chunk:
498
- on_chunk("content", text)
499
-
500
- elif event_type == "response.function_call_arguments.delta":
501
- delta = event.get("delta", "")
502
- current_tool.setdefault("arguments", "")
503
- current_tool["arguments"] += delta
504
-
505
- elif event_type == "response.output_item.added":
506
- item = event.get("item", {})
507
- if item.get("type") == "reasoning":
508
- saw_reasoning_item = True
509
- elif item.get("type") == "function_call":
510
- current_tool = {
511
- "id": item.get("call_id", ""),
512
- "name": item.get("name", ""),
513
- "arguments": "",
514
- }
515
-
516
- elif event_type == "response.output_item.done":
517
- item = event.get("item", {})
518
- if item.get("type") == "reasoning":
519
- saw_reasoning_item = True
520
- if not saw_reasoning_text:
521
- reasoning_text = _extract_reasoning_text_from_item(item)
522
- if reasoning_text:
523
- saw_reasoning_text = True
524
- thinking_parts.append(reasoning_text)
525
- if on_chunk:
526
- on_chunk("thinking", reasoning_text)
527
- elif item.get("type") == "function_call":
528
- call_id = item.get("call_id") or current_tool.get("id", "")
529
- name = item.get("name") or current_tool.get("name", "")
530
- args_str = item.get("arguments") or current_tool.get(
531
- "arguments", "{}"
532
- )
533
- try:
534
- args = json.loads(args_str)
535
- except json.JSONDecodeError:
536
- args = {}
537
- tool_calls.append(
538
- ToolCall(id=call_id, name=name, arguments=args)
539
- )
540
- current_tool = {}
541
-
542
- elif event_type == "response.completed":
543
- response_data = event.get("response", {})
544
- if isinstance(response_data, dict):
545
- reasoning_tokens = _extract_reasoning_tokens(response_data)
546
- response_usage = _extract_usage(response_data)
547
- usage_payload = response_data.get("usage")
548
- if isinstance(usage_payload, dict):
549
- raw_usage = dict(usage_payload)
550
- if not saw_reasoning_text:
551
- reasoning_text = _extract_reasoning_text_from_output(
552
- response_data.get("output")
553
- )
554
- if reasoning_text:
555
- saw_reasoning_text = True
556
- thinking_parts.append(reasoning_text)
557
- if on_chunk:
558
- on_chunk("thinking", reasoning_text)
559
- output_parts = _extract_output_parts(
560
- response_data.get("output")
561
- )
562
- if output_parts:
563
- response_parts = output_parts
564
- except RequestException as exc:
565
- elapsed = time.perf_counter() - t0
566
- logger.warning(
567
- "LLM API transport error [provider={}, model={}, type=openai_responses]: {} ({:.2f}s)",
568
- self._provider_name,
569
- self._model,
570
- exc,
571
- elapsed,
572
- )
573
- raise build_network_error(
574
- provider_name=self._provider_name,
575
- provider_type="openai_responses",
576
- model=self._model,
577
- base_url=self._api_base_url,
578
- error=exc,
579
- ) from exc
580
- finally:
581
- close_client = getattr(client, "close", None)
582
- if callable(close_client):
583
- close_client()
584
- self._client = create_http_session(
585
- timeout=self._request_timeout_seconds
586
- )
587
-
588
- elapsed = time.perf_counter() - t0
589
- content = "".join(content_parts) or None
590
- thinking = "".join(thinking_parts) or None
591
- if thinking is None and (saw_reasoning_item or reasoning_tokens > 0):
592
- thinking = _format_reasoning_fallback(reasoning_tokens)
593
-
594
- logger.debug(
595
- "[{}] OpenAI Responses chat done: {:.2f}s, chunks={}, content_len={}, thinking_len={}, tool_calls={}",
596
- self._provider_name,
597
- elapsed,
598
- chunk_count,
599
- len(content) if content else 0,
600
- len(thinking) if thinking else 0,
601
- len(tool_calls),
602
- )
603
-
604
- if tool_calls:
605
- return LLMResponse(
606
- content=content,
607
- parts=response_parts,
608
- tool_calls=tool_calls,
609
- thinking=thinking,
610
- usage=response_usage,
611
- raw_usage=raw_usage,
612
- )
613
-
614
- return LLMResponse(
615
- content=content or "",
616
- parts=response_parts,
617
- thinking=thinking,
618
- usage=response_usage,
619
- raw_usage=raw_usage,
620
- )
621
-
622
- def list_models(
623
- self,
624
- register_interrupt: Callable[[Callable[[], None] | None], None] | None = None,
625
- ) -> list[ModelInfo]:
626
- url = f"{self._api_base_url}/models"
627
- client = self._client
628
- close_client = getattr(client, "close", None)
629
- if register_interrupt is not None and callable(close_client):
630
- register_interrupt(close_client)
631
- try:
632
- resp = client.get(url, headers=self._headers())
633
- resp.raise_for_status()
634
- data = resp.json()
635
- models = data.get("data", [])
636
- return [
637
- build_model_info(
638
- provider_type="openai_responses",
639
- model_id=m["id"],
640
- )
641
- for m in models
642
- ]
643
- except Exception as e:
644
- logger.error(
645
- "Failed to list models [provider={}, type=openai_responses]: {}",
646
- self._provider_name,
647
- e,
648
- )
649
- return []
650
- finally:
651
- if callable(close_client):
652
- close_client()
653
- self._client = create_http_session(
654
- timeout=self._request_timeout_seconds
655
- )