flowent 0.0.7 → 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 (386) hide show
  1. package/README.md +0 -3
  2. package/backend/README.md +0 -3
  3. package/backend/pyproject.toml +2 -8
  4. package/backend/src/flowent/__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 +213 -3173
  17. package/backend/src/flowent/cli.py +19 -24
  18. package/backend/src/flowent/context.py +127 -0
  19. package/backend/src/flowent/llm.py +256 -0
  20. package/backend/src/flowent/logging.py +170 -129
  21. package/backend/src/flowent/main.py +321 -70
  22. package/backend/src/flowent/patch.py +182 -0
  23. package/backend/src/flowent/paths.py +11 -0
  24. package/backend/src/flowent/sandbox.py +214 -40
  25. package/backend/src/flowent/static/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
  26. package/backend/src/flowent/static/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
  27. package/backend/src/flowent/static/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
  28. package/backend/src/flowent/static/assets/index-C76K95ty.js +81 -0
  29. package/backend/src/flowent/static/assets/index-iUMNKvlU.css +2 -0
  30. package/backend/src/flowent/static/flowent.png +0 -0
  31. package/backend/src/flowent/static/index.html +5 -25
  32. package/backend/src/flowent/storage.py +302 -0
  33. package/backend/src/flowent/tools.py +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__/model_metadata.cpython-313.pyc +0 -0
  64. package/backend/src/flowent/__pycache__/network.cpython-313.pyc +0 -0
  65. package/backend/src/flowent/__pycache__/observability_service.cpython-313.pyc +0 -0
  66. package/backend/src/flowent/__pycache__/registry.cpython-313.pyc +0 -0
  67. package/backend/src/flowent/__pycache__/role_management.cpython-313.pyc +0 -0
  68. package/backend/src/flowent/__pycache__/runtime.cpython-313.pyc +0 -0
  69. package/backend/src/flowent/__pycache__/security.cpython-313.pyc +0 -0
  70. package/backend/src/flowent/__pycache__/settings.cpython-313.pyc +0 -0
  71. package/backend/src/flowent/__pycache__/settings_management.cpython-313.pyc +0 -0
  72. package/backend/src/flowent/__pycache__/state_db.cpython-313.pyc +0 -0
  73. package/backend/src/flowent/__pycache__/workspace_store.cpython-313.pyc +0 -0
  74. package/backend/src/flowent/access.py +0 -247
  75. package/backend/src/flowent/assistant_commands.py +0 -115
  76. package/backend/src/flowent/channels/__init__.py +0 -3
  77. package/backend/src/flowent/channels/__pycache__/__init__.cpython-313.pyc +0 -0
  78. package/backend/src/flowent/channels/__pycache__/telegram.cpython-313.pyc +0 -0
  79. package/backend/src/flowent/channels/telegram.py +0 -615
  80. package/backend/src/flowent/config.py +0 -14
  81. package/backend/src/flowent/dev.py +0 -3
  82. package/backend/src/flowent/events.py +0 -157
  83. package/backend/src/flowent/graph_runtime.py +0 -60
  84. package/backend/src/flowent/graph_service.py +0 -2401
  85. package/backend/src/flowent/image_assets.py +0 -356
  86. package/backend/src/flowent/model_metadata.py +0 -102
  87. package/backend/src/flowent/models/__init__.py +0 -125
  88. package/backend/src/flowent/models/__pycache__/__init__.cpython-313.pyc +0 -0
  89. package/backend/src/flowent/models/__pycache__/agent.cpython-313.pyc +0 -0
  90. package/backend/src/flowent/models/__pycache__/base.cpython-313.pyc +0 -0
  91. package/backend/src/flowent/models/__pycache__/blueprint.cpython-313.pyc +0 -0
  92. package/backend/src/flowent/models/__pycache__/content.cpython-313.pyc +0 -0
  93. package/backend/src/flowent/models/__pycache__/delta.cpython-313.pyc +0 -0
  94. package/backend/src/flowent/models/__pycache__/event.cpython-313.pyc +0 -0
  95. package/backend/src/flowent/models/__pycache__/graph.cpython-313.pyc +0 -0
  96. package/backend/src/flowent/models/__pycache__/history.cpython-313.pyc +0 -0
  97. package/backend/src/flowent/models/__pycache__/llm.cpython-313.pyc +0 -0
  98. package/backend/src/flowent/models/__pycache__/message.cpython-313.pyc +0 -0
  99. package/backend/src/flowent/models/__pycache__/tab.cpython-313.pyc +0 -0
  100. package/backend/src/flowent/models/__pycache__/todo.cpython-313.pyc +0 -0
  101. package/backend/src/flowent/models/agent.py +0 -34
  102. package/backend/src/flowent/models/base.py +0 -24
  103. package/backend/src/flowent/models/blueprint.py +0 -176
  104. package/backend/src/flowent/models/content.py +0 -164
  105. package/backend/src/flowent/models/delta.py +0 -44
  106. package/backend/src/flowent/models/event.py +0 -51
  107. package/backend/src/flowent/models/graph.py +0 -472
  108. package/backend/src/flowent/models/history.py +0 -272
  109. package/backend/src/flowent/models/llm.py +0 -62
  110. package/backend/src/flowent/models/message.py +0 -33
  111. package/backend/src/flowent/models/tab.py +0 -85
  112. package/backend/src/flowent/models/todo.py +0 -10
  113. package/backend/src/flowent/network.py +0 -146
  114. package/backend/src/flowent/observability_service.py +0 -218
  115. package/backend/src/flowent/prompts/__init__.py +0 -67
  116. package/backend/src/flowent/prompts/__pycache__/__init__.cpython-313.pyc +0 -0
  117. package/backend/src/flowent/prompts/__pycache__/common.cpython-313.pyc +0 -0
  118. package/backend/src/flowent/prompts/__pycache__/steward.cpython-313.pyc +0 -0
  119. package/backend/src/flowent/prompts/common.py +0 -250
  120. package/backend/src/flowent/prompts/steward.py +0 -64
  121. package/backend/src/flowent/providers/__init__.py +0 -23
  122. package/backend/src/flowent/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  123. package/backend/src/flowent/providers/__pycache__/anthropic.cpython-313.pyc +0 -0
  124. package/backend/src/flowent/providers/__pycache__/base_url.cpython-313.pyc +0 -0
  125. package/backend/src/flowent/providers/__pycache__/configuration.cpython-313.pyc +0 -0
  126. package/backend/src/flowent/providers/__pycache__/content.cpython-313.pyc +0 -0
  127. package/backend/src/flowent/providers/__pycache__/errors.cpython-313.pyc +0 -0
  128. package/backend/src/flowent/providers/__pycache__/gateway.cpython-313.pyc +0 -0
  129. package/backend/src/flowent/providers/__pycache__/headers.cpython-313.pyc +0 -0
  130. package/backend/src/flowent/providers/__pycache__/management.cpython-313.pyc +0 -0
  131. package/backend/src/flowent/providers/__pycache__/openai.cpython-313.pyc +0 -0
  132. package/backend/src/flowent/providers/__pycache__/openai_responses.cpython-313.pyc +0 -0
  133. package/backend/src/flowent/providers/__pycache__/registry.cpython-313.pyc +0 -0
  134. package/backend/src/flowent/providers/__pycache__/sse.cpython-313.pyc +0 -0
  135. package/backend/src/flowent/providers/__pycache__/thinking.cpython-313.pyc +0 -0
  136. package/backend/src/flowent/providers/anthropic.py +0 -468
  137. package/backend/src/flowent/providers/base_url.py +0 -60
  138. package/backend/src/flowent/providers/configuration.py +0 -189
  139. package/backend/src/flowent/providers/content.py +0 -122
  140. package/backend/src/flowent/providers/errors.py +0 -223
  141. package/backend/src/flowent/providers/gateway.py +0 -169
  142. package/backend/src/flowent/providers/gemini.py +0 -447
  143. package/backend/src/flowent/providers/headers.py +0 -20
  144. package/backend/src/flowent/providers/management.py +0 -96
  145. package/backend/src/flowent/providers/ollama.py +0 -293
  146. package/backend/src/flowent/providers/openai.py +0 -422
  147. package/backend/src/flowent/providers/openai_responses.py +0 -655
  148. package/backend/src/flowent/providers/registry.py +0 -144
  149. package/backend/src/flowent/providers/sse.py +0 -31
  150. package/backend/src/flowent/providers/thinking.py +0 -79
  151. package/backend/src/flowent/registry.py +0 -73
  152. package/backend/src/flowent/role_management.py +0 -270
  153. package/backend/src/flowent/routes/__init__.py +0 -26
  154. package/backend/src/flowent/routes/__pycache__/__init__.cpython-313.pyc +0 -0
  155. package/backend/src/flowent/routes/__pycache__/access.cpython-313.pyc +0 -0
  156. package/backend/src/flowent/routes/__pycache__/assistant.cpython-313.pyc +0 -0
  157. package/backend/src/flowent/routes/__pycache__/image_assets.cpython-313.pyc +0 -0
  158. package/backend/src/flowent/routes/__pycache__/meta.cpython-313.pyc +0 -0
  159. package/backend/src/flowent/routes/__pycache__/nodes.cpython-313.pyc +0 -0
  160. package/backend/src/flowent/routes/__pycache__/prompts.cpython-313.pyc +0 -0
  161. package/backend/src/flowent/routes/__pycache__/providers_route.cpython-313.pyc +0 -0
  162. package/backend/src/flowent/routes/__pycache__/roles.cpython-313.pyc +0 -0
  163. package/backend/src/flowent/routes/__pycache__/settings.cpython-313.pyc +0 -0
  164. package/backend/src/flowent/routes/__pycache__/tabs.cpython-313.pyc +0 -0
  165. package/backend/src/flowent/routes/__pycache__/ws.cpython-313.pyc +0 -0
  166. package/backend/src/flowent/routes/access.py +0 -48
  167. package/backend/src/flowent/routes/assistant.py +0 -158
  168. package/backend/src/flowent/routes/image_assets.py +0 -33
  169. package/backend/src/flowent/routes/meta.py +0 -28
  170. package/backend/src/flowent/routes/nodes.py +0 -423
  171. package/backend/src/flowent/routes/prompts.py +0 -46
  172. package/backend/src/flowent/routes/providers_route.py +0 -365
  173. package/backend/src/flowent/routes/roles.py +0 -207
  174. package/backend/src/flowent/routes/settings.py +0 -379
  175. package/backend/src/flowent/routes/tabs.py +0 -298
  176. package/backend/src/flowent/routes/ws.py +0 -33
  177. package/backend/src/flowent/runtime.py +0 -160
  178. package/backend/src/flowent/security.py +0 -37
  179. package/backend/src/flowent/settings.py +0 -2112
  180. package/backend/src/flowent/settings_management.py +0 -394
  181. package/backend/src/flowent/state_db.py +0 -108
  182. package/backend/src/flowent/static/assets/AssistantPage-BW7XAd9I.js +0 -1
  183. package/backend/src/flowent/static/assets/ChannelsPage-tCJHgt6m.js +0 -1
  184. package/backend/src/flowent/static/assets/PageScaffold-f6g2l7XN.js +0 -1
  185. package/backend/src/flowent/static/assets/PromptsPage-C3Sxn2D7.js +0 -1
  186. package/backend/src/flowent/static/assets/ProvidersPage-BfmdXmNt.js +0 -3
  187. package/backend/src/flowent/static/assets/RolesPage-DET8wO4r.js +0 -1
  188. package/backend/src/flowent/static/assets/SettingsPage-D-g3deMm.js +0 -3
  189. package/backend/src/flowent/static/assets/ToolsPage-CDmtE2g4.js +0 -1
  190. package/backend/src/flowent/static/assets/WorkspacePage-AZsJ0sD0.js +0 -3
  191. package/backend/src/flowent/static/assets/WorkspacePanels-CteCjolX.js +0 -1
  192. package/backend/src/flowent/static/assets/alert-dialog-Duorp_S-.js +0 -1
  193. package/backend/src/flowent/static/assets/dialog-C3ixjGjN.js +0 -1
  194. package/backend/src/flowent/static/assets/elk-worker.min-C9JGDOE-.js +0 -6312
  195. package/backend/src/flowent/static/assets/graph-vendor-CHpVij2M.css +0 -1
  196. package/backend/src/flowent/static/assets/graph-vendor-DRq_-6fV.js +0 -7
  197. package/backend/src/flowent/static/assets/index--o_0fv0N.css +0 -1
  198. package/backend/src/flowent/static/assets/index-C9HuekJm.js +0 -10
  199. package/backend/src/flowent/static/assets/layout.worker-jMHqAFbP.js +0 -24
  200. package/backend/src/flowent/static/assets/markdown-vendor-C9RtvaJh.js +0 -29
  201. package/backend/src/flowent/static/assets/modelParams-DmnF2hwR.js +0 -1
  202. package/backend/src/flowent/static/assets/providerTypes-DT3Ahwl_.js +0 -1
  203. package/backend/src/flowent/static/assets/react-vendor-mEs_JJxa.js +0 -9
  204. package/backend/src/flowent/static/assets/roles-CuRT_chR.js +0 -1
  205. package/backend/src/flowent/static/assets/rolldown-runtime-BYbx6iT9.js +0 -1
  206. package/backend/src/flowent/static/assets/select-DCfeNu-F.js +0 -1
  207. package/backend/src/flowent/static/assets/surface-pWwG5ogx.js +0 -1
  208. package/backend/src/flowent/static/assets/ui-vendor-C5pJa8N7.js +0 -51
  209. package/backend/src/flowent/static/assets/useAppRoute-FgSHBKhV.js +0 -1
  210. package/backend/src/flowent/static/favicon.svg +0 -4
  211. package/backend/src/flowent/tools/__init__.py +0 -176
  212. package/backend/src/flowent/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  213. package/backend/src/flowent/tools/__pycache__/connect.cpython-313.pyc +0 -0
  214. package/backend/src/flowent/tools/__pycache__/contacts.cpython-313.pyc +0 -0
  215. package/backend/src/flowent/tools/__pycache__/create_agent.cpython-313.pyc +0 -0
  216. package/backend/src/flowent/tools/__pycache__/create_tab.cpython-313.pyc +0 -0
  217. package/backend/src/flowent/tools/__pycache__/delete_tab.cpython-313.pyc +0 -0
  218. package/backend/src/flowent/tools/__pycache__/edit.cpython-313.pyc +0 -0
  219. package/backend/src/flowent/tools/__pycache__/exec.cpython-313.pyc +0 -0
  220. package/backend/src/flowent/tools/__pycache__/fetch.cpython-313.pyc +0 -0
  221. package/backend/src/flowent/tools/__pycache__/idle.cpython-313.pyc +0 -0
  222. package/backend/src/flowent/tools/__pycache__/list_roles.cpython-313.pyc +0 -0
  223. package/backend/src/flowent/tools/__pycache__/list_tabs.cpython-313.pyc +0 -0
  224. package/backend/src/flowent/tools/__pycache__/list_tools.cpython-313.pyc +0 -0
  225. package/backend/src/flowent/tools/__pycache__/manage_prompts.cpython-313.pyc +0 -0
  226. package/backend/src/flowent/tools/__pycache__/manage_providers.cpython-313.pyc +0 -0
  227. package/backend/src/flowent/tools/__pycache__/manage_roles.cpython-313.pyc +0 -0
  228. package/backend/src/flowent/tools/__pycache__/manage_settings.cpython-313.pyc +0 -0
  229. package/backend/src/flowent/tools/__pycache__/read.cpython-313.pyc +0 -0
  230. package/backend/src/flowent/tools/__pycache__/send.cpython-313.pyc +0 -0
  231. package/backend/src/flowent/tools/__pycache__/set_permissions.cpython-313.pyc +0 -0
  232. package/backend/src/flowent/tools/__pycache__/sleep.cpython-313.pyc +0 -0
  233. package/backend/src/flowent/tools/__pycache__/todo.cpython-313.pyc +0 -0
  234. package/backend/src/flowent/tools/connect.py +0 -100
  235. package/backend/src/flowent/tools/contacts.py +0 -22
  236. package/backend/src/flowent/tools/create_agent.py +0 -191
  237. package/backend/src/flowent/tools/create_tab.py +0 -61
  238. package/backend/src/flowent/tools/delete_tab.py +0 -39
  239. package/backend/src/flowent/tools/edit.py +0 -142
  240. package/backend/src/flowent/tools/exec.py +0 -118
  241. package/backend/src/flowent/tools/fetch.py +0 -85
  242. package/backend/src/flowent/tools/idle.py +0 -27
  243. package/backend/src/flowent/tools/list_roles.py +0 -68
  244. package/backend/src/flowent/tools/list_tabs.py +0 -100
  245. package/backend/src/flowent/tools/list_tools.py +0 -28
  246. package/backend/src/flowent/tools/manage_prompts.py +0 -102
  247. package/backend/src/flowent/tools/manage_providers.py +0 -220
  248. package/backend/src/flowent/tools/manage_roles.py +0 -275
  249. package/backend/src/flowent/tools/manage_settings.py +0 -326
  250. package/backend/src/flowent/tools/read.py +0 -152
  251. package/backend/src/flowent/tools/send.py +0 -68
  252. package/backend/src/flowent/tools/set_permissions.py +0 -99
  253. package/backend/src/flowent/tools/sleep.py +0 -41
  254. package/backend/src/flowent/tools/todo.py +0 -51
  255. package/backend/src/flowent/workspace_store.py +0 -479
  256. package/backend/tests/__init__.py +0 -0
  257. package/backend/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  258. package/backend/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  259. package/backend/tests/conftest.py +0 -6
  260. package/backend/tests/integration/api/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  261. package/backend/tests/integration/api/__pycache__/test_access_api.cpython-313-pytest-9.0.3.pyc +0 -0
  262. package/backend/tests/integration/api/__pycache__/test_assistant_api.cpython-313-pytest-9.0.3.pyc +0 -0
  263. package/backend/tests/integration/api/__pycache__/test_frontend_mounting.cpython-313-pytest-9.0.3.pyc +0 -0
  264. package/backend/tests/integration/api/__pycache__/test_meta_api.cpython-313-pytest-9.0.3.pyc +0 -0
  265. package/backend/tests/integration/api/__pycache__/test_nodes_api.cpython-313-pytest-9.0.3.pyc +0 -0
  266. package/backend/tests/integration/api/__pycache__/test_prompts_api.cpython-313-pytest-9.0.3.pyc +0 -0
  267. package/backend/tests/integration/api/__pycache__/test_roles_api.cpython-313-pytest-9.0.3.pyc +0 -0
  268. package/backend/tests/integration/api/__pycache__/test_tabs_api.cpython-313-pytest-9.0.3.pyc +0 -0
  269. package/backend/tests/integration/api/conftest.py +0 -29
  270. package/backend/tests/integration/api/test_access_api.py +0 -182
  271. package/backend/tests/integration/api/test_assistant_api.py +0 -422
  272. package/backend/tests/integration/api/test_frontend_mounting.py +0 -61
  273. package/backend/tests/integration/api/test_meta_api.py +0 -32
  274. package/backend/tests/integration/api/test_nodes_api.py +0 -787
  275. package/backend/tests/integration/api/test_prompts_api.py +0 -47
  276. package/backend/tests/integration/api/test_roles_api.py +0 -228
  277. package/backend/tests/integration/api/test_tabs_api.py +0 -688
  278. package/backend/tests/unit/__pycache__/test_access.cpython-313-pytest-9.0.3.pyc +0 -0
  279. package/backend/tests/unit/__pycache__/test_cli.cpython-313-pytest-9.0.3.pyc +0 -0
  280. package/backend/tests/unit/__pycache__/test_graph_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  281. package/backend/tests/unit/__pycache__/test_network.cpython-313-pytest-9.0.3.pyc +0 -0
  282. package/backend/tests/unit/__pycache__/test_state_sqlite_storage.cpython-313-pytest-9.0.3.pyc +0 -0
  283. package/backend/tests/unit/__pycache__/test_workspace_store.cpython-313-pytest-9.0.3.pyc +0 -0
  284. package/backend/tests/unit/agent/__pycache__/test_agent_public_api.cpython-313-pytest-9.0.3.pyc +0 -0
  285. package/backend/tests/unit/agent/__pycache__/test_agent_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  286. package/backend/tests/unit/agent/test_agent_public_api.py +0 -822
  287. package/backend/tests/unit/agent/test_agent_runtime.py +0 -3088
  288. package/backend/tests/unit/channels/__pycache__/test_telegram_channel.cpython-313-pytest-9.0.3.pyc +0 -0
  289. package/backend/tests/unit/channels/test_telegram_channel.py +0 -552
  290. package/backend/tests/unit/logging/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
  291. package/backend/tests/unit/logging/test_logging.py +0 -132
  292. package/backend/tests/unit/prompts/__pycache__/test_prompts.cpython-313-pytest-9.0.3.pyc +0 -0
  293. package/backend/tests/unit/prompts/test_prompts.py +0 -570
  294. package/backend/tests/unit/providers/__pycache__/test_anthropic_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  295. package/backend/tests/unit/providers/__pycache__/test_errors.cpython-313-pytest-9.0.3.pyc +0 -0
  296. package/backend/tests/unit/providers/__pycache__/test_extract_delta_parts.cpython-313-pytest-9.0.3.pyc +0 -0
  297. package/backend/tests/unit/providers/__pycache__/test_openai_provider.cpython-313-pytest-9.0.3.pyc +0 -0
  298. package/backend/tests/unit/providers/__pycache__/test_openai_responses.cpython-313-pytest-9.0.3.pyc +0 -0
  299. package/backend/tests/unit/providers/__pycache__/test_provider_gateway.cpython-313-pytest-9.0.3.pyc +0 -0
  300. package/backend/tests/unit/providers/__pycache__/test_think_tag_parser.cpython-313-pytest-9.0.3.pyc +0 -0
  301. package/backend/tests/unit/providers/test_anthropic_provider.py +0 -185
  302. package/backend/tests/unit/providers/test_errors.py +0 -68
  303. package/backend/tests/unit/providers/test_extract_delta_parts.py +0 -22
  304. package/backend/tests/unit/providers/test_openai_provider.py +0 -139
  305. package/backend/tests/unit/providers/test_openai_responses.py +0 -402
  306. package/backend/tests/unit/providers/test_provider_gateway.py +0 -359
  307. package/backend/tests/unit/providers/test_think_tag_parser.py +0 -36
  308. package/backend/tests/unit/routes/__pycache__/test_prompts_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  309. package/backend/tests/unit/routes/__pycache__/test_providers_route.cpython-313-pytest-9.0.3.pyc +0 -0
  310. package/backend/tests/unit/routes/__pycache__/test_roles_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  311. package/backend/tests/unit/routes/__pycache__/test_settings_routes.cpython-313-pytest-9.0.3.pyc +0 -0
  312. package/backend/tests/unit/routes/test_prompts_routes.py +0 -82
  313. package/backend/tests/unit/routes/test_providers_route.py +0 -370
  314. package/backend/tests/unit/routes/test_roles_routes.py +0 -539
  315. package/backend/tests/unit/routes/test_settings_routes.py +0 -1123
  316. package/backend/tests/unit/runtime/__pycache__/test_bootstrap_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
  317. package/backend/tests/unit/runtime/test_bootstrap_runtime.py +0 -1002
  318. package/backend/tests/unit/sandbox/__pycache__/test_sandbox_tools.cpython-313-pytest-9.0.3.pyc +0 -0
  319. package/backend/tests/unit/sandbox/test_sandbox_tools.py +0 -78
  320. package/backend/tests/unit/security/__pycache__/test_security.cpython-313-pytest-9.0.3.pyc +0 -0
  321. package/backend/tests/unit/security/test_security.py +0 -124
  322. package/backend/tests/unit/settings/__pycache__/test_settings_roles.cpython-313-pytest-9.0.3.pyc +0 -0
  323. package/backend/tests/unit/settings/test_settings_roles.py +0 -703
  324. package/backend/tests/unit/test_access.py +0 -45
  325. package/backend/tests/unit/test_cli.py +0 -102
  326. package/backend/tests/unit/test_graph_runtime.py +0 -72
  327. package/backend/tests/unit/test_network.py +0 -51
  328. package/backend/tests/unit/test_state_sqlite_storage.py +0 -87
  329. package/backend/tests/unit/test_workspace_store.py +0 -228
  330. package/backend/tests/unit/tools/__pycache__/test_connect_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  331. package/backend/tests/unit/tools/__pycache__/test_create_agent_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  332. package/backend/tests/unit/tools/__pycache__/test_delete_tab_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  333. package/backend/tests/unit/tools/__pycache__/test_edit_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  334. package/backend/tests/unit/tools/__pycache__/test_exec_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  335. package/backend/tests/unit/tools/__pycache__/test_fetch_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  336. package/backend/tests/unit/tools/__pycache__/test_manage_prompts_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  337. package/backend/tests/unit/tools/__pycache__/test_manage_providers_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  338. package/backend/tests/unit/tools/__pycache__/test_manage_roles_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  339. package/backend/tests/unit/tools/__pycache__/test_manage_settings_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  340. package/backend/tests/unit/tools/__pycache__/test_read_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  341. package/backend/tests/unit/tools/__pycache__/test_set_permissions_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  342. package/backend/tests/unit/tools/__pycache__/test_todo_tool.cpython-313-pytest-9.0.3.pyc +0 -0
  343. package/backend/tests/unit/tools/__pycache__/test_tool_registry.cpython-313-pytest-9.0.3.pyc +0 -0
  344. package/backend/tests/unit/tools/test_connect_tool.py +0 -228
  345. package/backend/tests/unit/tools/test_create_agent_tool.py +0 -404
  346. package/backend/tests/unit/tools/test_delete_tab_tool.py +0 -116
  347. package/backend/tests/unit/tools/test_edit_tool.py +0 -115
  348. package/backend/tests/unit/tools/test_exec_tool.py +0 -81
  349. package/backend/tests/unit/tools/test_fetch_tool.py +0 -65
  350. package/backend/tests/unit/tools/test_manage_prompts_tool.py +0 -92
  351. package/backend/tests/unit/tools/test_manage_providers_tool.py +0 -460
  352. package/backend/tests/unit/tools/test_manage_roles_tool.py +0 -411
  353. package/backend/tests/unit/tools/test_manage_settings_tool.py +0 -611
  354. package/backend/tests/unit/tools/test_read_tool.py +0 -33
  355. package/backend/tests/unit/tools/test_set_permissions_tool.py +0 -595
  356. package/backend/tests/unit/tools/test_todo_tool.py +0 -37
  357. package/backend/tests/unit/tools/test_tool_registry.py +0 -199
  358. package/dist/frontend/assets/AssistantPage-BW7XAd9I.js +0 -1
  359. package/dist/frontend/assets/ChannelsPage-tCJHgt6m.js +0 -1
  360. package/dist/frontend/assets/PageScaffold-f6g2l7XN.js +0 -1
  361. package/dist/frontend/assets/PromptsPage-C3Sxn2D7.js +0 -1
  362. package/dist/frontend/assets/ProvidersPage-BfmdXmNt.js +0 -3
  363. package/dist/frontend/assets/RolesPage-DET8wO4r.js +0 -1
  364. package/dist/frontend/assets/SettingsPage-D-g3deMm.js +0 -3
  365. package/dist/frontend/assets/ToolsPage-CDmtE2g4.js +0 -1
  366. package/dist/frontend/assets/WorkspacePage-AZsJ0sD0.js +0 -3
  367. package/dist/frontend/assets/WorkspacePanels-CteCjolX.js +0 -1
  368. package/dist/frontend/assets/alert-dialog-Duorp_S-.js +0 -1
  369. package/dist/frontend/assets/dialog-C3ixjGjN.js +0 -1
  370. package/dist/frontend/assets/elk-worker.min-C9JGDOE-.js +0 -6312
  371. package/dist/frontend/assets/graph-vendor-CHpVij2M.css +0 -1
  372. package/dist/frontend/assets/graph-vendor-DRq_-6fV.js +0 -7
  373. package/dist/frontend/assets/index--o_0fv0N.css +0 -1
  374. package/dist/frontend/assets/index-C9HuekJm.js +0 -10
  375. package/dist/frontend/assets/layout.worker-jMHqAFbP.js +0 -24
  376. package/dist/frontend/assets/markdown-vendor-C9RtvaJh.js +0 -29
  377. package/dist/frontend/assets/modelParams-DmnF2hwR.js +0 -1
  378. package/dist/frontend/assets/providerTypes-DT3Ahwl_.js +0 -1
  379. package/dist/frontend/assets/react-vendor-mEs_JJxa.js +0 -9
  380. package/dist/frontend/assets/roles-CuRT_chR.js +0 -1
  381. package/dist/frontend/assets/rolldown-runtime-BYbx6iT9.js +0 -1
  382. package/dist/frontend/assets/select-DCfeNu-F.js +0 -1
  383. package/dist/frontend/assets/surface-pWwG5ogx.js +0 -1
  384. package/dist/frontend/assets/ui-vendor-C5pJa8N7.js +0 -51
  385. package/dist/frontend/assets/useAppRoute-FgSHBKhV.js +0 -1
  386. 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
- )