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.
- package/README.md +1 -4
- package/backend/README.md +1 -4
- package/backend/pyproject.toml +2 -8
- package/backend/src/flowent/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/agent.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/cli.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/context.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/llm.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/logging.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/main.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/patch.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/paths.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/sandbox.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/storage.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/tools.cpython-313.pyc +0 -0
- package/backend/src/flowent/agent.py +217 -3094
- package/backend/src/flowent/cli.py +19 -24
- package/backend/src/flowent/context.py +127 -0
- package/backend/src/flowent/llm.py +256 -0
- package/backend/src/flowent/logging.py +170 -129
- package/backend/src/flowent/main.py +321 -70
- package/backend/src/flowent/patch.py +182 -0
- package/backend/src/flowent/paths.py +11 -0
- package/backend/src/flowent/sandbox.py +214 -40
- package/backend/src/flowent/static/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
- package/backend/src/flowent/static/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
- package/backend/src/flowent/static/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
- package/backend/src/flowent/static/assets/index-C76K95ty.js +81 -0
- package/backend/src/flowent/static/assets/index-iUMNKvlU.css +2 -0
- package/backend/src/flowent/static/flowent.png +0 -0
- package/backend/src/flowent/static/index.html +5 -25
- package/backend/src/flowent/storage.py +302 -0
- package/backend/src/flowent/tools.py +364 -0
- package/backend/tests/__pycache__/test_agent_tools.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/__pycache__/test_health.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/__pycache__/test_llm_providers.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/__pycache__/test_persistence.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/__pycache__/test_workspace_chat.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/test_agent_tools.py +449 -0
- package/backend/tests/test_health.py +12 -0
- package/backend/tests/test_llm_providers.py +113 -0
- package/backend/tests/test_logging.py +182 -0
- package/backend/tests/test_persistence.py +125 -0
- package/backend/tests/test_workspace_chat.py +578 -0
- package/backend/uv.lock +803 -99
- package/dist/frontend/assets/geist-cyrillic-wght-normal-CHSlOQsW.woff2 +0 -0
- package/dist/frontend/assets/geist-latin-ext-wght-normal-DMtmJ5ZE.woff2 +0 -0
- package/dist/frontend/assets/geist-latin-wght-normal-Dm3htQBi.woff2 +0 -0
- package/dist/frontend/assets/index-C76K95ty.js +81 -0
- package/dist/frontend/assets/index-iUMNKvlU.css +2 -0
- package/dist/frontend/flowent.png +0 -0
- package/dist/frontend/index.html +5 -25
- package/package.json +1 -2
- package/backend/src/flowent/__pycache__/_version.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/access.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/assistant_commands.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/config.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/events.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/graph_runtime.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/graph_service.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/image_assets.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/mcp_service.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/model_metadata.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/network.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/observability_service.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/registry.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/role_management.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/runtime.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/security.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/settings.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/settings_management.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/state_db.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/workspace_store.cpython-313.pyc +0 -0
- package/backend/src/flowent/access.py +0 -247
- package/backend/src/flowent/assistant_commands.py +0 -115
- package/backend/src/flowent/channels/__init__.py +0 -3
- package/backend/src/flowent/channels/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/channels/__pycache__/telegram.cpython-313.pyc +0 -0
- package/backend/src/flowent/channels/telegram.py +0 -615
- package/backend/src/flowent/config.py +0 -14
- package/backend/src/flowent/dev.py +0 -3
- package/backend/src/flowent/events.py +0 -157
- package/backend/src/flowent/graph_runtime.py +0 -60
- package/backend/src/flowent/graph_service.py +0 -2508
- package/backend/src/flowent/image_assets.py +0 -356
- package/backend/src/flowent/mcp_service.py +0 -1918
- package/backend/src/flowent/model_metadata.py +0 -102
- package/backend/src/flowent/models/__init__.py +0 -125
- package/backend/src/flowent/models/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/agent.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/base.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/blueprint.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/content.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/delta.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/event.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/graph.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/history.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/llm.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/message.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/tab.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/__pycache__/todo.cpython-313.pyc +0 -0
- package/backend/src/flowent/models/agent.py +0 -34
- package/backend/src/flowent/models/base.py +0 -24
- package/backend/src/flowent/models/blueprint.py +0 -176
- package/backend/src/flowent/models/content.py +0 -164
- package/backend/src/flowent/models/delta.py +0 -44
- package/backend/src/flowent/models/event.py +0 -51
- package/backend/src/flowent/models/graph.py +0 -472
- package/backend/src/flowent/models/history.py +0 -272
- package/backend/src/flowent/models/llm.py +0 -62
- package/backend/src/flowent/models/message.py +0 -33
- package/backend/src/flowent/models/tab.py +0 -85
- package/backend/src/flowent/models/todo.py +0 -10
- package/backend/src/flowent/network.py +0 -146
- package/backend/src/flowent/observability_service.py +0 -218
- package/backend/src/flowent/prompts/__init__.py +0 -67
- package/backend/src/flowent/prompts/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/prompts/__pycache__/common.cpython-313.pyc +0 -0
- package/backend/src/flowent/prompts/__pycache__/steward.cpython-313.pyc +0 -0
- package/backend/src/flowent/prompts/common.py +0 -250
- package/backend/src/flowent/prompts/steward.py +0 -64
- package/backend/src/flowent/providers/__init__.py +0 -23
- package/backend/src/flowent/providers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/anthropic.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/base_url.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/configuration.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/content.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/errors.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/gateway.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/headers.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/management.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/openai.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/openai_responses.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/registry.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/sse.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/__pycache__/thinking.cpython-313.pyc +0 -0
- package/backend/src/flowent/providers/anthropic.py +0 -468
- package/backend/src/flowent/providers/base_url.py +0 -60
- package/backend/src/flowent/providers/configuration.py +0 -189
- package/backend/src/flowent/providers/content.py +0 -122
- package/backend/src/flowent/providers/errors.py +0 -223
- package/backend/src/flowent/providers/gateway.py +0 -169
- package/backend/src/flowent/providers/gemini.py +0 -447
- package/backend/src/flowent/providers/headers.py +0 -20
- package/backend/src/flowent/providers/management.py +0 -96
- package/backend/src/flowent/providers/ollama.py +0 -293
- package/backend/src/flowent/providers/openai.py +0 -422
- package/backend/src/flowent/providers/openai_responses.py +0 -655
- package/backend/src/flowent/providers/registry.py +0 -144
- package/backend/src/flowent/providers/sse.py +0 -31
- package/backend/src/flowent/providers/thinking.py +0 -79
- package/backend/src/flowent/registry.py +0 -73
- package/backend/src/flowent/role_management.py +0 -267
- package/backend/src/flowent/routes/__init__.py +0 -28
- package/backend/src/flowent/routes/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/access.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/assistant.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/image_assets.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/mcp.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/meta.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/nodes.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/prompts.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/providers_route.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/roles.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/settings.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/tabs.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/__pycache__/ws.cpython-313.pyc +0 -0
- package/backend/src/flowent/routes/access.py +0 -48
- package/backend/src/flowent/routes/assistant.py +0 -155
- package/backend/src/flowent/routes/image_assets.py +0 -33
- package/backend/src/flowent/routes/mcp.py +0 -125
- package/backend/src/flowent/routes/meta.py +0 -28
- package/backend/src/flowent/routes/nodes.py +0 -413
- package/backend/src/flowent/routes/prompts.py +0 -46
- package/backend/src/flowent/routes/providers_route.py +0 -365
- package/backend/src/flowent/routes/roles.py +0 -207
- package/backend/src/flowent/routes/settings.py +0 -328
- package/backend/src/flowent/routes/tabs.py +0 -310
- package/backend/src/flowent/routes/ws.py +0 -33
- package/backend/src/flowent/runtime.py +0 -165
- package/backend/src/flowent/security.py +0 -57
- package/backend/src/flowent/settings.py +0 -2518
- package/backend/src/flowent/settings_management.py +0 -298
- package/backend/src/flowent/state_db.py +0 -120
- package/backend/src/flowent/static/assets/AssistantPage-VBohhz4d.js +0 -1
- package/backend/src/flowent/static/assets/ChannelsPage-CIydPZA_.js +0 -1
- package/backend/src/flowent/static/assets/McpPage-CHPm2TPY.js +0 -7
- package/backend/src/flowent/static/assets/PageScaffold-DteOA8V7.js +0 -1
- package/backend/src/flowent/static/assets/PromptsPage-CSmJ3sZg.js +0 -1
- package/backend/src/flowent/static/assets/ProvidersPage-sl2jeG4e.js +0 -3
- package/backend/src/flowent/static/assets/RolesPage-DCe7W6Km.js +0 -1
- package/backend/src/flowent/static/assets/SettingsPage-Bix9e63E.js +0 -3
- package/backend/src/flowent/static/assets/ToolsPage-favNkj5C.js +0 -1
- package/backend/src/flowent/static/assets/WorkspaceCommandDialog-DRS6wiD6.js +0 -1
- package/backend/src/flowent/static/assets/WorkspacePage-KuaDjt_D.js +0 -3
- package/backend/src/flowent/static/assets/WorkspacePanels-BZxBw8M5.js +0 -1
- package/backend/src/flowent/static/assets/alert-dialog-DIBUCmqM.js +0 -1
- package/backend/src/flowent/static/assets/datetime-eJqd0V2S.js +0 -1
- package/backend/src/flowent/static/assets/dialog-BOvHIBrg.js +0 -1
- package/backend/src/flowent/static/assets/elk-worker.min-C9JGDOE-.js +0 -6312
- package/backend/src/flowent/static/assets/graph-vendor-CHpVij2M.css +0 -1
- package/backend/src/flowent/static/assets/graph-vendor-DRq_-6fV.js +0 -7
- package/backend/src/flowent/static/assets/index-Biio-CoI.js +0 -10
- package/backend/src/flowent/static/assets/index-CmQvO7sl.css +0 -1
- package/backend/src/flowent/static/assets/layout.worker-jMHqAFbP.js +0 -24
- package/backend/src/flowent/static/assets/markdown-vendor-C9RtvaJh.js +0 -29
- package/backend/src/flowent/static/assets/modelParams-DcEhGnu0.js +0 -1
- package/backend/src/flowent/static/assets/react-vendor-mEs_JJxa.js +0 -9
- package/backend/src/flowent/static/assets/roles-BbIEIMeG.js +0 -1
- package/backend/src/flowent/static/assets/rolldown-runtime-BYbx6iT9.js +0 -1
- package/backend/src/flowent/static/assets/select-D9SwnlXF.js +0 -1
- package/backend/src/flowent/static/assets/surface-Bzr1FRG4.js +0 -1
- package/backend/src/flowent/static/assets/triState-DgLlKdRR.js +0 -1
- package/backend/src/flowent/static/assets/ui-vendor-UazN8rcv.js +0 -51
- package/backend/src/flowent/static/favicon.svg +0 -4
- package/backend/src/flowent/tools/__init__.py +0 -275
- package/backend/src/flowent/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/connect.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/contacts.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/create_agent.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/create_tab.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/delete_tab.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/edit.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/exec.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/fetch.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/idle.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/list_roles.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/list_tabs.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/list_tools.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/manage_prompts.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/manage_providers.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/manage_roles.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/manage_settings.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/mcp.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/read.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/send.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/set_permissions.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/sleep.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/__pycache__/todo.cpython-313.pyc +0 -0
- package/backend/src/flowent/tools/connect.py +0 -100
- package/backend/src/flowent/tools/contacts.py +0 -22
- package/backend/src/flowent/tools/create_agent.py +0 -191
- package/backend/src/flowent/tools/create_tab.py +0 -61
- package/backend/src/flowent/tools/delete_tab.py +0 -39
- package/backend/src/flowent/tools/edit.py +0 -142
- package/backend/src/flowent/tools/exec.py +0 -118
- package/backend/src/flowent/tools/fetch.py +0 -85
- package/backend/src/flowent/tools/idle.py +0 -27
- package/backend/src/flowent/tools/list_roles.py +0 -75
- package/backend/src/flowent/tools/list_tabs.py +0 -100
- package/backend/src/flowent/tools/list_tools.py +0 -28
- package/backend/src/flowent/tools/manage_prompts.py +0 -102
- package/backend/src/flowent/tools/manage_providers.py +0 -220
- package/backend/src/flowent/tools/manage_roles.py +0 -275
- package/backend/src/flowent/tools/manage_settings.py +0 -364
- package/backend/src/flowent/tools/mcp.py +0 -199
- package/backend/src/flowent/tools/read.py +0 -152
- package/backend/src/flowent/tools/send.py +0 -68
- package/backend/src/flowent/tools/set_permissions.py +0 -99
- package/backend/src/flowent/tools/sleep.py +0 -41
- package/backend/src/flowent/tools/todo.py +0 -51
- package/backend/src/flowent/workspace_store.py +0 -479
- package/backend/tests/__init__.py +0 -0
- package/backend/tests/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/conftest.py +0 -6
- package/backend/tests/integration/api/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_access_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_assistant_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_frontend_mounting.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_mcp_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_meta_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_nodes_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_prompts_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_roles_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/__pycache__/test_tabs_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/integration/api/conftest.py +0 -29
- package/backend/tests/integration/api/test_access_api.py +0 -182
- package/backend/tests/integration/api/test_assistant_api.py +0 -354
- package/backend/tests/integration/api/test_frontend_mounting.py +0 -61
- package/backend/tests/integration/api/test_mcp_api.py +0 -116
- package/backend/tests/integration/api/test_meta_api.py +0 -33
- package/backend/tests/integration/api/test_nodes_api.py +0 -722
- package/backend/tests/integration/api/test_prompts_api.py +0 -47
- package/backend/tests/integration/api/test_roles_api.py +0 -228
- package/backend/tests/integration/api/test_tabs_api.py +0 -802
- package/backend/tests/unit/__pycache__/test_access.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/__pycache__/test_cli.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/__pycache__/test_graph_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/__pycache__/test_network.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/__pycache__/test_state_sqlite_storage.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/__pycache__/test_workspace_store.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/agent/__pycache__/test_agent_public_api.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/agent/__pycache__/test_agent_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/agent/test_agent_public_api.py +0 -837
- package/backend/tests/unit/agent/test_agent_runtime.py +0 -2942
- package/backend/tests/unit/channels/__pycache__/test_telegram_channel.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/channels/test_telegram_channel.py +0 -552
- package/backend/tests/unit/logging/__pycache__/test_logging.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/logging/test_logging.py +0 -132
- package/backend/tests/unit/prompts/__pycache__/test_prompts.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/prompts/test_prompts.py +0 -570
- package/backend/tests/unit/providers/__pycache__/test_anthropic_provider.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/providers/__pycache__/test_errors.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/providers/__pycache__/test_extract_delta_parts.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/providers/__pycache__/test_openai_provider.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/providers/__pycache__/test_openai_responses.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/providers/__pycache__/test_provider_gateway.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/providers/__pycache__/test_think_tag_parser.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/providers/test_anthropic_provider.py +0 -185
- package/backend/tests/unit/providers/test_errors.py +0 -68
- package/backend/tests/unit/providers/test_extract_delta_parts.py +0 -22
- package/backend/tests/unit/providers/test_openai_provider.py +0 -139
- package/backend/tests/unit/providers/test_openai_responses.py +0 -402
- package/backend/tests/unit/providers/test_provider_gateway.py +0 -359
- package/backend/tests/unit/providers/test_think_tag_parser.py +0 -36
- package/backend/tests/unit/routes/__pycache__/test_prompts_routes.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/routes/__pycache__/test_providers_route.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/routes/__pycache__/test_roles_routes.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/routes/__pycache__/test_settings_routes.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/routes/test_prompts_routes.py +0 -104
- package/backend/tests/unit/routes/test_providers_route.py +0 -370
- package/backend/tests/unit/routes/test_roles_routes.py +0 -535
- package/backend/tests/unit/routes/test_settings_routes.py +0 -1142
- package/backend/tests/unit/runtime/__pycache__/test_bootstrap_runtime.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/runtime/test_bootstrap_runtime.py +0 -1002
- package/backend/tests/unit/sandbox/__pycache__/test_sandbox_tools.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/sandbox/test_sandbox_tools.py +0 -78
- package/backend/tests/unit/security/__pycache__/test_security.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/security/test_security.py +0 -124
- package/backend/tests/unit/settings/__pycache__/test_settings_roles.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/settings/test_settings_roles.py +0 -751
- package/backend/tests/unit/test_access.py +0 -45
- package/backend/tests/unit/test_cli.py +0 -124
- package/backend/tests/unit/test_graph_runtime.py +0 -72
- package/backend/tests/unit/test_network.py +0 -51
- package/backend/tests/unit/test_state_sqlite_storage.py +0 -159
- package/backend/tests/unit/test_workspace_store.py +0 -231
- package/backend/tests/unit/tools/__pycache__/test_connect_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_create_agent_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_delete_tab_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_edit_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_exec_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_fetch_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_manage_prompts_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_manage_providers_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_manage_roles_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_manage_settings_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_read_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_set_permissions_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_todo_tool.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/__pycache__/test_tool_registry.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/unit/tools/test_connect_tool.py +0 -228
- package/backend/tests/unit/tools/test_create_agent_tool.py +0 -436
- package/backend/tests/unit/tools/test_delete_tab_tool.py +0 -116
- package/backend/tests/unit/tools/test_edit_tool.py +0 -115
- package/backend/tests/unit/tools/test_exec_tool.py +0 -81
- package/backend/tests/unit/tools/test_fetch_tool.py +0 -65
- package/backend/tests/unit/tools/test_manage_prompts_tool.py +0 -117
- package/backend/tests/unit/tools/test_manage_providers_tool.py +0 -460
- package/backend/tests/unit/tools/test_manage_roles_tool.py +0 -411
- package/backend/tests/unit/tools/test_manage_settings_tool.py +0 -611
- package/backend/tests/unit/tools/test_read_tool.py +0 -33
- package/backend/tests/unit/tools/test_set_permissions_tool.py +0 -595
- package/backend/tests/unit/tools/test_todo_tool.py +0 -37
- package/backend/tests/unit/tools/test_tool_registry.py +0 -194
- package/dist/frontend/assets/AssistantPage-VBohhz4d.js +0 -1
- package/dist/frontend/assets/ChannelsPage-CIydPZA_.js +0 -1
- package/dist/frontend/assets/McpPage-CHPm2TPY.js +0 -7
- package/dist/frontend/assets/PageScaffold-DteOA8V7.js +0 -1
- package/dist/frontend/assets/PromptsPage-CSmJ3sZg.js +0 -1
- package/dist/frontend/assets/ProvidersPage-sl2jeG4e.js +0 -3
- package/dist/frontend/assets/RolesPage-DCe7W6Km.js +0 -1
- package/dist/frontend/assets/SettingsPage-Bix9e63E.js +0 -3
- package/dist/frontend/assets/ToolsPage-favNkj5C.js +0 -1
- package/dist/frontend/assets/WorkspaceCommandDialog-DRS6wiD6.js +0 -1
- package/dist/frontend/assets/WorkspacePage-KuaDjt_D.js +0 -3
- package/dist/frontend/assets/WorkspacePanels-BZxBw8M5.js +0 -1
- package/dist/frontend/assets/alert-dialog-DIBUCmqM.js +0 -1
- package/dist/frontend/assets/datetime-eJqd0V2S.js +0 -1
- package/dist/frontend/assets/dialog-BOvHIBrg.js +0 -1
- package/dist/frontend/assets/elk-worker.min-C9JGDOE-.js +0 -6312
- package/dist/frontend/assets/graph-vendor-CHpVij2M.css +0 -1
- package/dist/frontend/assets/graph-vendor-DRq_-6fV.js +0 -7
- package/dist/frontend/assets/index-Biio-CoI.js +0 -10
- package/dist/frontend/assets/index-CmQvO7sl.css +0 -1
- package/dist/frontend/assets/layout.worker-jMHqAFbP.js +0 -24
- package/dist/frontend/assets/markdown-vendor-C9RtvaJh.js +0 -29
- package/dist/frontend/assets/modelParams-DcEhGnu0.js +0 -1
- package/dist/frontend/assets/react-vendor-mEs_JJxa.js +0 -9
- package/dist/frontend/assets/roles-BbIEIMeG.js +0 -1
- package/dist/frontend/assets/rolldown-runtime-BYbx6iT9.js +0 -1
- package/dist/frontend/assets/select-D9SwnlXF.js +0 -1
- package/dist/frontend/assets/surface-Bzr1FRG4.js +0 -1
- package/dist/frontend/assets/triState-DgLlKdRR.js +0 -1
- package/dist/frontend/assets/ui-vendor-UazN8rcv.js +0 -51
- package/dist/frontend/favicon.svg +0 -4
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import time
|
|
4
|
-
from copy import deepcopy
|
|
5
|
-
|
|
6
|
-
from fastapi import APIRouter, HTTPException
|
|
7
|
-
from loguru import logger
|
|
8
|
-
from pydantic import BaseModel, ConfigDict
|
|
9
|
-
|
|
10
|
-
from flowent.access import initialize_live_access_signature, set_access_code
|
|
11
|
-
from flowent.events import event_bus
|
|
12
|
-
from flowent.settings import (
|
|
13
|
-
TelegramApprovedChat,
|
|
14
|
-
TelegramSettings,
|
|
15
|
-
get_settings,
|
|
16
|
-
save_settings,
|
|
17
|
-
serialize_provider,
|
|
18
|
-
serialize_role,
|
|
19
|
-
serialize_settings,
|
|
20
|
-
serialize_telegram_settings,
|
|
21
|
-
)
|
|
22
|
-
from flowent.settings_management import (
|
|
23
|
-
MISSING,
|
|
24
|
-
apply_resolved_settings_update,
|
|
25
|
-
resolve_settings_update,
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
router = APIRouter()
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@router.get("/api/settings/bootstrap")
|
|
32
|
-
async def get_settings_bootstrap() -> dict[str, object]:
|
|
33
|
-
from flowent._version import __version__
|
|
34
|
-
|
|
35
|
-
settings = get_settings()
|
|
36
|
-
return {
|
|
37
|
-
"settings": serialize_settings(settings),
|
|
38
|
-
"providers": [serialize_provider(provider) for provider in settings.providers],
|
|
39
|
-
"roles": [serialize_role(role) for role in settings.roles],
|
|
40
|
-
"version": __version__,
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@router.get("/api/settings")
|
|
45
|
-
async def get_settings_api() -> dict[str, object]:
|
|
46
|
-
settings = get_settings()
|
|
47
|
-
return serialize_settings(settings)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class UpdateSettingsRequest(BaseModel):
|
|
51
|
-
model_config = ConfigDict(extra="forbid")
|
|
52
|
-
access: dict[str, object] | None = None
|
|
53
|
-
assistant: dict[str, object] | None = None
|
|
54
|
-
event_log: dict[str, object] | None = None
|
|
55
|
-
leader: dict[str, object] | None = None
|
|
56
|
-
model: dict[str, object] | None = None
|
|
57
|
-
working_dir: str | None = None
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class UpdateTelegramSettingsRequest(BaseModel):
|
|
61
|
-
model_config = ConfigDict(extra="forbid")
|
|
62
|
-
bot_token: str | None = None
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@router.post("/api/settings")
|
|
66
|
-
async def update_settings(req: UpdateSettingsRequest) -> dict[str, object]:
|
|
67
|
-
from flowent.graph_service import sync_assistant_role, sync_tab_leaders
|
|
68
|
-
from flowent.providers.gateway import gateway
|
|
69
|
-
|
|
70
|
-
source_settings = get_settings()
|
|
71
|
-
current = deepcopy(source_settings)
|
|
72
|
-
next_access_code: str | None = None
|
|
73
|
-
reauth_required = False
|
|
74
|
-
|
|
75
|
-
if req.access is not None:
|
|
76
|
-
raw_new_code = req.access.get("new_code", "")
|
|
77
|
-
raw_confirm_code = req.access.get("confirm_code", "")
|
|
78
|
-
if raw_new_code is not None and not isinstance(raw_new_code, str):
|
|
79
|
-
raise HTTPException(
|
|
80
|
-
status_code=400, detail="access.new_code must be a string"
|
|
81
|
-
)
|
|
82
|
-
if raw_confirm_code is not None and not isinstance(raw_confirm_code, str):
|
|
83
|
-
raise HTTPException(
|
|
84
|
-
status_code=400,
|
|
85
|
-
detail="access.confirm_code must be a string",
|
|
86
|
-
)
|
|
87
|
-
new_code = raw_new_code if isinstance(raw_new_code, str) else ""
|
|
88
|
-
confirm_code = raw_confirm_code if isinstance(raw_confirm_code, str) else ""
|
|
89
|
-
if new_code or confirm_code:
|
|
90
|
-
if not new_code.strip():
|
|
91
|
-
raise HTTPException(
|
|
92
|
-
status_code=400,
|
|
93
|
-
detail="access.new_code must not be empty",
|
|
94
|
-
)
|
|
95
|
-
if confirm_code != new_code:
|
|
96
|
-
raise HTTPException(
|
|
97
|
-
status_code=400,
|
|
98
|
-
detail="access.confirm_code must match access.new_code",
|
|
99
|
-
)
|
|
100
|
-
next_access_code = new_code
|
|
101
|
-
|
|
102
|
-
assistant_role_name: str | None = None
|
|
103
|
-
assistant_allow_network: object = MISSING
|
|
104
|
-
assistant_write_dirs: object = MISSING
|
|
105
|
-
if req.assistant is not None:
|
|
106
|
-
assistant_unknown_fields = sorted(
|
|
107
|
-
set(req.assistant) - {"role_name", "allow_network", "write_dirs"}
|
|
108
|
-
)
|
|
109
|
-
if assistant_unknown_fields:
|
|
110
|
-
raise HTTPException(
|
|
111
|
-
status_code=400,
|
|
112
|
-
detail=(
|
|
113
|
-
"Unknown assistant fields: " + ", ".join(assistant_unknown_fields)
|
|
114
|
-
),
|
|
115
|
-
)
|
|
116
|
-
if "allow_network" in req.assistant:
|
|
117
|
-
assistant_allow_network = req.assistant.get("allow_network")
|
|
118
|
-
if "write_dirs" in req.assistant:
|
|
119
|
-
assistant_write_dirs = req.assistant.get("write_dirs")
|
|
120
|
-
raw_role_name = req.assistant.get("role_name")
|
|
121
|
-
if isinstance(raw_role_name, str) and raw_role_name.strip():
|
|
122
|
-
assistant_role_name = raw_role_name
|
|
123
|
-
|
|
124
|
-
leader_role_name: str | None = None
|
|
125
|
-
if req.leader is not None:
|
|
126
|
-
raw_role_name = req.leader.get("role_name")
|
|
127
|
-
if isinstance(raw_role_name, str) and raw_role_name.strip():
|
|
128
|
-
leader_role_name = raw_role_name
|
|
129
|
-
|
|
130
|
-
timestamp_format: str | None = None
|
|
131
|
-
if req.event_log is not None:
|
|
132
|
-
raw_timestamp_format = req.event_log.get("timestamp_format")
|
|
133
|
-
if isinstance(raw_timestamp_format, str):
|
|
134
|
-
timestamp_format = raw_timestamp_format
|
|
135
|
-
|
|
136
|
-
active_provider_id: str | None = None
|
|
137
|
-
active_model: str | None = None
|
|
138
|
-
context_window_tokens: object = MISSING
|
|
139
|
-
input_image: object = MISSING
|
|
140
|
-
output_image: object = MISSING
|
|
141
|
-
structured_output: object = MISSING
|
|
142
|
-
timeout_ms: object = MISSING
|
|
143
|
-
retry_policy: object = MISSING
|
|
144
|
-
max_retries: object = MISSING
|
|
145
|
-
retry_initial_delay_seconds: object = MISSING
|
|
146
|
-
retry_max_delay_seconds: object = MISSING
|
|
147
|
-
retry_backoff_cap_retries: object = MISSING
|
|
148
|
-
auto_compact_token_limit: object = MISSING
|
|
149
|
-
model_params: object = MISSING
|
|
150
|
-
if req.model is not None:
|
|
151
|
-
raw_active_provider_id = req.model.get("active_provider_id")
|
|
152
|
-
if isinstance(raw_active_provider_id, str):
|
|
153
|
-
active_provider_id = raw_active_provider_id
|
|
154
|
-
raw_active_model = req.model.get("active_model")
|
|
155
|
-
if isinstance(raw_active_model, str):
|
|
156
|
-
active_model = raw_active_model
|
|
157
|
-
if "context_window_tokens" in req.model:
|
|
158
|
-
context_window_tokens = req.model.get("context_window_tokens")
|
|
159
|
-
if "input_image" in req.model:
|
|
160
|
-
input_image = req.model.get("input_image")
|
|
161
|
-
if "output_image" in req.model:
|
|
162
|
-
output_image = req.model.get("output_image")
|
|
163
|
-
if "structured_output" in req.model:
|
|
164
|
-
structured_output = req.model.get("structured_output")
|
|
165
|
-
if "timeout_ms" in req.model:
|
|
166
|
-
timeout_ms = req.model.get("timeout_ms")
|
|
167
|
-
if "retry_policy" in req.model:
|
|
168
|
-
retry_policy = req.model.get("retry_policy")
|
|
169
|
-
if "max_retries" in req.model:
|
|
170
|
-
max_retries = req.model.get("max_retries")
|
|
171
|
-
if "retry_initial_delay_seconds" in req.model:
|
|
172
|
-
retry_initial_delay_seconds = req.model.get("retry_initial_delay_seconds")
|
|
173
|
-
if "retry_max_delay_seconds" in req.model:
|
|
174
|
-
retry_max_delay_seconds = req.model.get("retry_max_delay_seconds")
|
|
175
|
-
if "retry_backoff_cap_retries" in req.model:
|
|
176
|
-
retry_backoff_cap_retries = req.model.get("retry_backoff_cap_retries")
|
|
177
|
-
if "auto_compact_token_limit" in req.model:
|
|
178
|
-
auto_compact_token_limit = req.model.get("auto_compact_token_limit")
|
|
179
|
-
if "params" in req.model:
|
|
180
|
-
model_params = req.model.get("params")
|
|
181
|
-
|
|
182
|
-
try:
|
|
183
|
-
resolved = resolve_settings_update(
|
|
184
|
-
current,
|
|
185
|
-
working_dir=req.working_dir,
|
|
186
|
-
assistant_role_name=assistant_role_name,
|
|
187
|
-
assistant_allow_network=assistant_allow_network,
|
|
188
|
-
assistant_write_dirs=assistant_write_dirs,
|
|
189
|
-
leader_role_name=leader_role_name,
|
|
190
|
-
active_provider_id=active_provider_id,
|
|
191
|
-
active_model=active_model,
|
|
192
|
-
context_window_tokens=context_window_tokens,
|
|
193
|
-
input_image=input_image,
|
|
194
|
-
output_image=output_image,
|
|
195
|
-
structured_output=structured_output,
|
|
196
|
-
max_retries=max_retries,
|
|
197
|
-
retry_policy=retry_policy,
|
|
198
|
-
timeout_ms=timeout_ms,
|
|
199
|
-
retry_initial_delay_seconds=retry_initial_delay_seconds,
|
|
200
|
-
retry_max_delay_seconds=retry_max_delay_seconds,
|
|
201
|
-
retry_backoff_cap_retries=retry_backoff_cap_retries,
|
|
202
|
-
auto_compact_token_limit=auto_compact_token_limit,
|
|
203
|
-
model_params=model_params,
|
|
204
|
-
timestamp_format=timestamp_format,
|
|
205
|
-
)
|
|
206
|
-
except ValueError as exc:
|
|
207
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
208
|
-
|
|
209
|
-
apply_resolved_settings_update(current, resolved)
|
|
210
|
-
|
|
211
|
-
if next_access_code is not None:
|
|
212
|
-
set_access_code(current, next_access_code)
|
|
213
|
-
reauth_required = True
|
|
214
|
-
|
|
215
|
-
save_settings(current)
|
|
216
|
-
source_settings.__dict__.clear()
|
|
217
|
-
source_settings.__dict__.update(deepcopy(current).__dict__)
|
|
218
|
-
initialize_live_access_signature()
|
|
219
|
-
try:
|
|
220
|
-
sync_assistant_role(reason="assistant settings updated")
|
|
221
|
-
sync_tab_leaders(reason="leader settings updated")
|
|
222
|
-
gateway.invalidate_cache()
|
|
223
|
-
except Exception:
|
|
224
|
-
logger.exception("Settings saved but runtime synchronization failed")
|
|
225
|
-
if reauth_required:
|
|
226
|
-
event_bus.close_all_connections(code=4001, reason="Access code rotated")
|
|
227
|
-
logger.info("Settings updated")
|
|
228
|
-
return {
|
|
229
|
-
"status": "saved",
|
|
230
|
-
"settings": serialize_settings(current),
|
|
231
|
-
"reauth_required": reauth_required,
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
@router.get("/api/settings/telegram")
|
|
236
|
-
async def get_telegram_settings() -> dict[str, object]:
|
|
237
|
-
settings = get_settings()
|
|
238
|
-
return serialize_telegram_settings(settings.telegram)
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
@router.patch("/api/settings/telegram")
|
|
242
|
-
async def update_telegram_settings(
|
|
243
|
-
req: UpdateTelegramSettingsRequest,
|
|
244
|
-
) -> dict[str, object]:
|
|
245
|
-
from flowent.runtime import restart_telegram_channel
|
|
246
|
-
|
|
247
|
-
settings = get_settings()
|
|
248
|
-
previous_token = settings.telegram.bot_token
|
|
249
|
-
|
|
250
|
-
next_token = previous_token
|
|
251
|
-
if req.bot_token is not None:
|
|
252
|
-
next_token = req.bot_token.strip()
|
|
253
|
-
|
|
254
|
-
settings.telegram = TelegramSettings(
|
|
255
|
-
bot_token=next_token,
|
|
256
|
-
pending_chats=list(settings.telegram.pending_chats),
|
|
257
|
-
approved_chats=list(settings.telegram.approved_chats),
|
|
258
|
-
)
|
|
259
|
-
save_settings(settings)
|
|
260
|
-
|
|
261
|
-
if next_token != previous_token:
|
|
262
|
-
restart_telegram_channel()
|
|
263
|
-
|
|
264
|
-
logger.info("Telegram settings updated")
|
|
265
|
-
return {
|
|
266
|
-
"status": "saved",
|
|
267
|
-
"telegram": serialize_telegram_settings(settings.telegram),
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
@router.post("/api/settings/telegram/approve/{chat_id}")
|
|
272
|
-
async def approve_telegram_chat(chat_id: int) -> dict[str, object]:
|
|
273
|
-
settings = get_settings()
|
|
274
|
-
pending_chat = next(
|
|
275
|
-
(chat for chat in settings.telegram.pending_chats if chat.chat_id == chat_id),
|
|
276
|
-
None,
|
|
277
|
-
)
|
|
278
|
-
if pending_chat is None:
|
|
279
|
-
raise HTTPException(status_code=404, detail="Pending Telegram chat not found")
|
|
280
|
-
|
|
281
|
-
settings.telegram.pending_chats = [
|
|
282
|
-
chat for chat in settings.telegram.pending_chats if chat.chat_id != chat_id
|
|
283
|
-
]
|
|
284
|
-
if not any(chat.chat_id == chat_id for chat in settings.telegram.approved_chats):
|
|
285
|
-
settings.telegram.approved_chats.append(
|
|
286
|
-
TelegramApprovedChat(
|
|
287
|
-
chat_id=pending_chat.chat_id,
|
|
288
|
-
username=pending_chat.username,
|
|
289
|
-
display_name=pending_chat.display_name,
|
|
290
|
-
approved_at=time.time(),
|
|
291
|
-
)
|
|
292
|
-
)
|
|
293
|
-
save_settings(settings)
|
|
294
|
-
logger.info("Telegram chat approved: {}", chat_id)
|
|
295
|
-
return {
|
|
296
|
-
"status": "approved",
|
|
297
|
-
"telegram": serialize_telegram_settings(settings.telegram),
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
@router.delete("/api/settings/telegram/pending/{chat_id}")
|
|
302
|
-
async def delete_pending_telegram_chat(chat_id: int) -> dict[str, object]:
|
|
303
|
-
settings = get_settings()
|
|
304
|
-
settings.telegram.pending_chats = [
|
|
305
|
-
chat for chat in settings.telegram.pending_chats if chat.chat_id != chat_id
|
|
306
|
-
]
|
|
307
|
-
save_settings(settings)
|
|
308
|
-
logger.info("Pending Telegram chat removed: {}", chat_id)
|
|
309
|
-
return {
|
|
310
|
-
"status": "deleted",
|
|
311
|
-
"telegram": serialize_telegram_settings(settings.telegram),
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
@router.delete("/api/settings/telegram/chat/{chat_id}")
|
|
316
|
-
async def delete_telegram_chat(chat_id: int) -> dict[str, object]:
|
|
317
|
-
settings = get_settings()
|
|
318
|
-
settings.telegram.approved_chats = [
|
|
319
|
-
existing_chat
|
|
320
|
-
for existing_chat in settings.telegram.approved_chats
|
|
321
|
-
if existing_chat.chat_id != chat_id
|
|
322
|
-
]
|
|
323
|
-
save_settings(settings)
|
|
324
|
-
logger.info("Telegram chat removed: {}", chat_id)
|
|
325
|
-
return {
|
|
326
|
-
"status": "deleted",
|
|
327
|
-
"telegram": serialize_telegram_settings(settings.telegram),
|
|
328
|
-
}
|
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from fastapi import APIRouter, HTTPException
|
|
4
|
-
from pydantic import BaseModel, ConfigDict
|
|
5
|
-
|
|
6
|
-
from flowent.graph_service import (
|
|
7
|
-
activate_tab,
|
|
8
|
-
create_agent_node,
|
|
9
|
-
create_edge,
|
|
10
|
-
create_graph_node,
|
|
11
|
-
create_tab,
|
|
12
|
-
deactivate_tab,
|
|
13
|
-
delete_agent_node,
|
|
14
|
-
delete_edge,
|
|
15
|
-
delete_tab,
|
|
16
|
-
duplicate_tab,
|
|
17
|
-
list_node_connection_ids,
|
|
18
|
-
list_tab_edges,
|
|
19
|
-
list_workflow_nodes,
|
|
20
|
-
serialize_tab_summary,
|
|
21
|
-
update_tab_definition,
|
|
22
|
-
)
|
|
23
|
-
from flowent.models import AgentState, WorkflowNodeKind
|
|
24
|
-
from flowent.registry import registry
|
|
25
|
-
from flowent.workspace_store import workspace_store
|
|
26
|
-
|
|
27
|
-
router = APIRouter()
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class CreateTabRequest(BaseModel):
|
|
31
|
-
model_config = ConfigDict(extra="forbid")
|
|
32
|
-
title: str
|
|
33
|
-
allow_network: bool = False
|
|
34
|
-
write_dirs: list[str] = []
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class CreateTabNodeRequest(BaseModel):
|
|
38
|
-
model_config = ConfigDict(extra="forbid")
|
|
39
|
-
node_type: str = WorkflowNodeKind.AGENT.value
|
|
40
|
-
role_name: str | None = None
|
|
41
|
-
name: str | None = None
|
|
42
|
-
config: dict[str, object] = {}
|
|
43
|
-
tools: list[str] = []
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class CreateTabEdgeRequest(BaseModel):
|
|
47
|
-
model_config = ConfigDict(extra="forbid")
|
|
48
|
-
from_node_id: str
|
|
49
|
-
from_port_key: str = "out"
|
|
50
|
-
to_node_id: str
|
|
51
|
-
to_port_key: str = "in"
|
|
52
|
-
kind: str | None = None
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class UpdateTabDefinitionRequest(BaseModel):
|
|
56
|
-
model_config = ConfigDict(extra="forbid")
|
|
57
|
-
definition: dict[str, object]
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def _serialize_workflow_node(
|
|
61
|
-
*,
|
|
62
|
-
tab_id: str,
|
|
63
|
-
node_id: str,
|
|
64
|
-
) -> dict[str, object]:
|
|
65
|
-
tab = workspace_store.get_tab(tab_id)
|
|
66
|
-
if tab is None:
|
|
67
|
-
raise HTTPException(status_code=404, detail="Workflow not found")
|
|
68
|
-
definition = tab.definition.get_node(node_id)
|
|
69
|
-
if definition is None:
|
|
70
|
-
raise HTTPException(status_code=404, detail="Node not found")
|
|
71
|
-
record = workspace_store.get_node_record(node_id)
|
|
72
|
-
live_node = registry.get(node_id)
|
|
73
|
-
config = dict(definition.config)
|
|
74
|
-
role_name = config.get("role_name")
|
|
75
|
-
name = config.get("name")
|
|
76
|
-
position = tab.definition.view.positions.get(node_id)
|
|
77
|
-
todos = (
|
|
78
|
-
[todo.serialize() for todo in live_node.get_todos_snapshot()]
|
|
79
|
-
if live_node is not None
|
|
80
|
-
else [todo.serialize() for todo in (record.todos if record is not None else [])]
|
|
81
|
-
)
|
|
82
|
-
state = (
|
|
83
|
-
live_node.state.value
|
|
84
|
-
if live_node is not None
|
|
85
|
-
else (record.state.value if record is not None else AgentState.IDLE.value)
|
|
86
|
-
)
|
|
87
|
-
return {
|
|
88
|
-
"id": definition.id,
|
|
89
|
-
"node_type": definition.type.value,
|
|
90
|
-
"workflow_id": tab_id,
|
|
91
|
-
"role_name": role_name if isinstance(role_name, str) else None,
|
|
92
|
-
"is_leader": False,
|
|
93
|
-
"state": state,
|
|
94
|
-
"connections": list_node_connection_ids(tab_id=tab_id, node_id=definition.id)
|
|
95
|
-
if definition.type == WorkflowNodeKind.AGENT
|
|
96
|
-
else [],
|
|
97
|
-
"name": name if isinstance(name, str) else None,
|
|
98
|
-
"todos": todos if definition.type == WorkflowNodeKind.AGENT else [],
|
|
99
|
-
"position": position.serialize() if position is not None else None,
|
|
100
|
-
"config": config,
|
|
101
|
-
"inputs": [port.serialize() for port in definition.inputs],
|
|
102
|
-
"outputs": [port.serialize() for port in definition.outputs],
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
@router.get("/api/workflows")
|
|
107
|
-
async def list_workflows() -> dict[str, object]:
|
|
108
|
-
tabs = workspace_store.list_tabs()
|
|
109
|
-
return {"workflows": [serialize_tab_summary(tab) for tab in tabs]}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
@router.post("/api/workflows")
|
|
113
|
-
async def create_workflow_route(req: CreateTabRequest) -> dict[str, object]:
|
|
114
|
-
title = req.title.strip()
|
|
115
|
-
if not title:
|
|
116
|
-
raise HTTPException(status_code=400, detail="title must not be empty")
|
|
117
|
-
try:
|
|
118
|
-
tab = create_tab(
|
|
119
|
-
title=title,
|
|
120
|
-
allow_network=req.allow_network,
|
|
121
|
-
write_dirs=req.write_dirs,
|
|
122
|
-
)
|
|
123
|
-
except ValueError as exc:
|
|
124
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
125
|
-
return serialize_tab_summary(tab)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
@router.post("/api/workflows/{tab_id}/duplicate")
|
|
129
|
-
async def duplicate_workflow_route(tab_id: str) -> dict[str, object]:
|
|
130
|
-
duplicated, error = duplicate_tab(tab_id=tab_id)
|
|
131
|
-
if error is not None or duplicated is None:
|
|
132
|
-
raise HTTPException(
|
|
133
|
-
status_code=404 if error and error.endswith("not found") else 400,
|
|
134
|
-
detail=error or "Failed to duplicate workflow",
|
|
135
|
-
)
|
|
136
|
-
return serialize_tab_summary(duplicated)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
@router.post("/api/workflows/{tab_id}/activate")
|
|
140
|
-
async def activate_workflow_route(tab_id: str) -> dict[str, object]:
|
|
141
|
-
updated, errors, error = activate_tab(tab_id=tab_id, actor_id=tab_id)
|
|
142
|
-
if errors:
|
|
143
|
-
raise HTTPException(status_code=400, detail={"errors": errors})
|
|
144
|
-
if error is not None or updated is None:
|
|
145
|
-
raise HTTPException(
|
|
146
|
-
status_code=404 if error and error.endswith("not found") else 400,
|
|
147
|
-
detail=error or "Failed to activate workflow",
|
|
148
|
-
)
|
|
149
|
-
return serialize_tab_summary(updated)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
@router.post("/api/workflows/{tab_id}/deactivate")
|
|
153
|
-
async def deactivate_workflow_route(tab_id: str) -> dict[str, object]:
|
|
154
|
-
updated, error = deactivate_tab(tab_id=tab_id, actor_id=tab_id)
|
|
155
|
-
if error is not None or updated is None:
|
|
156
|
-
raise HTTPException(
|
|
157
|
-
status_code=404 if error and error.endswith("not found") else 400,
|
|
158
|
-
detail=error or "Failed to deactivate workflow",
|
|
159
|
-
)
|
|
160
|
-
return serialize_tab_summary(updated)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
@router.get("/api/workflows/{tab_id}")
|
|
164
|
-
async def get_workflow(tab_id: str) -> dict[str, object]:
|
|
165
|
-
tab = workspace_store.get_tab(tab_id)
|
|
166
|
-
if tab is None:
|
|
167
|
-
raise HTTPException(status_code=404, detail="Workflow not found")
|
|
168
|
-
nodes = [
|
|
169
|
-
_serialize_workflow_node(tab_id=tab_id, node_id=node.id)
|
|
170
|
-
for node in list_workflow_nodes(tab_id)
|
|
171
|
-
]
|
|
172
|
-
edges = [edge.serialize() for edge in list_tab_edges(tab_id)]
|
|
173
|
-
return {
|
|
174
|
-
"workflow": serialize_tab_summary(tab),
|
|
175
|
-
"nodes": nodes,
|
|
176
|
-
"edges": edges,
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
@router.put("/api/workflows/{tab_id}/definition")
|
|
181
|
-
async def update_workflow_definition_route(
|
|
182
|
-
tab_id: str,
|
|
183
|
-
req: UpdateTabDefinitionRequest,
|
|
184
|
-
) -> dict[str, object]:
|
|
185
|
-
updated, error = update_tab_definition(
|
|
186
|
-
tab_id=tab_id,
|
|
187
|
-
definition_payload=req.definition,
|
|
188
|
-
actor_id=tab_id,
|
|
189
|
-
)
|
|
190
|
-
if error is not None or updated is None:
|
|
191
|
-
raise HTTPException(
|
|
192
|
-
status_code=400 if error and not error.endswith("not found") else 404,
|
|
193
|
-
detail=error or "Failed to update workflow definition",
|
|
194
|
-
)
|
|
195
|
-
return serialize_tab_summary(updated)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
@router.delete("/api/workflows/{tab_id}")
|
|
199
|
-
async def delete_workflow_route(tab_id: str) -> dict[str, object]:
|
|
200
|
-
deleted, error = delete_tab(tab_id=tab_id)
|
|
201
|
-
if error is not None or deleted is None:
|
|
202
|
-
status_code = 404 if error and error.endswith("not found") else 400
|
|
203
|
-
raise HTTPException(
|
|
204
|
-
status_code=status_code,
|
|
205
|
-
detail=error or "Failed to delete workflow",
|
|
206
|
-
)
|
|
207
|
-
return deleted
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
@router.post("/api/workflows/{tab_id}/nodes")
|
|
211
|
-
async def create_workflow_node(
|
|
212
|
-
tab_id: str,
|
|
213
|
-
req: CreateTabNodeRequest,
|
|
214
|
-
) -> dict[str, object]:
|
|
215
|
-
try:
|
|
216
|
-
node_type = WorkflowNodeKind(req.node_type.strip())
|
|
217
|
-
except ValueError as exc:
|
|
218
|
-
raise HTTPException(status_code=400, detail="Invalid node_type") from exc
|
|
219
|
-
|
|
220
|
-
if node_type == WorkflowNodeKind.AGENT:
|
|
221
|
-
if not isinstance(req.role_name, str) or not req.role_name.strip():
|
|
222
|
-
raise HTTPException(status_code=400, detail="role_name is required")
|
|
223
|
-
record, error = create_agent_node(
|
|
224
|
-
role_name=req.role_name,
|
|
225
|
-
tab_id=tab_id,
|
|
226
|
-
name=req.name,
|
|
227
|
-
tools=req.tools,
|
|
228
|
-
)
|
|
229
|
-
if error is not None or record is None:
|
|
230
|
-
raise HTTPException(
|
|
231
|
-
status_code=400, detail=error or "Failed to create node"
|
|
232
|
-
)
|
|
233
|
-
return _serialize_workflow_node(tab_id=tab_id, node_id=record.id)
|
|
234
|
-
|
|
235
|
-
node, error = create_graph_node(
|
|
236
|
-
tab_id=tab_id,
|
|
237
|
-
node_type=node_type,
|
|
238
|
-
config={
|
|
239
|
-
**req.config,
|
|
240
|
-
**(
|
|
241
|
-
{"name": req.name}
|
|
242
|
-
if isinstance(req.name, str) and req.name.strip()
|
|
243
|
-
else {}
|
|
244
|
-
),
|
|
245
|
-
},
|
|
246
|
-
actor_id=tab_id,
|
|
247
|
-
)
|
|
248
|
-
if error is not None or node is None:
|
|
249
|
-
raise HTTPException(status_code=400, detail=error or "Failed to create node")
|
|
250
|
-
return _serialize_workflow_node(tab_id=tab_id, node_id=node.id)
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
@router.post("/api/workflows/{tab_id}/edges")
|
|
254
|
-
async def create_workflow_edge(
|
|
255
|
-
tab_id: str,
|
|
256
|
-
req: CreateTabEdgeRequest,
|
|
257
|
-
) -> dict[str, object]:
|
|
258
|
-
tab = workspace_store.get_tab(tab_id)
|
|
259
|
-
if tab is None:
|
|
260
|
-
raise HTTPException(status_code=404, detail="Workflow not found")
|
|
261
|
-
edge, error = create_edge(
|
|
262
|
-
tab_id=tab_id,
|
|
263
|
-
from_node_id=req.from_node_id,
|
|
264
|
-
from_port_key=req.from_port_key,
|
|
265
|
-
to_node_id=req.to_node_id,
|
|
266
|
-
to_port_key=req.to_port_key,
|
|
267
|
-
kind=req.kind or "",
|
|
268
|
-
)
|
|
269
|
-
if error is not None or edge is None:
|
|
270
|
-
raise HTTPException(status_code=400, detail=error or "Failed to create edge")
|
|
271
|
-
return edge.serialize()
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
@router.delete("/api/workflows/{tab_id}/nodes/{node_id}")
|
|
275
|
-
async def delete_workflow_node(tab_id: str, node_id: str) -> dict[str, object]:
|
|
276
|
-
deleted, error = delete_agent_node(
|
|
277
|
-
tab_id=tab_id,
|
|
278
|
-
node_id=node_id,
|
|
279
|
-
)
|
|
280
|
-
if error is not None or deleted is None:
|
|
281
|
-
status_code = 404 if error and error.endswith("not found") else 400
|
|
282
|
-
raise HTTPException(
|
|
283
|
-
status_code=status_code, detail=error or "Failed to delete node"
|
|
284
|
-
)
|
|
285
|
-
return deleted
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
@router.delete("/api/workflows/{tab_id}/edges")
|
|
289
|
-
async def delete_workflow_edge(
|
|
290
|
-
tab_id: str,
|
|
291
|
-
edge_id: str | None = None,
|
|
292
|
-
from_node_id: str | None = None,
|
|
293
|
-
to_node_id: str | None = None,
|
|
294
|
-
from_port_key: str | None = None,
|
|
295
|
-
to_port_key: str | None = None,
|
|
296
|
-
) -> dict[str, object]:
|
|
297
|
-
deleted, error = delete_edge(
|
|
298
|
-
tab_id=tab_id,
|
|
299
|
-
edge_id=edge_id,
|
|
300
|
-
from_node_id=from_node_id,
|
|
301
|
-
to_node_id=to_node_id,
|
|
302
|
-
from_port_key=from_port_key,
|
|
303
|
-
to_port_key=to_port_key,
|
|
304
|
-
)
|
|
305
|
-
if error is not None or deleted is None:
|
|
306
|
-
status_code = 404 if error and error.endswith("not found") else 400
|
|
307
|
-
raise HTTPException(
|
|
308
|
-
status_code=status_code, detail=error or "Failed to delete edge"
|
|
309
|
-
)
|
|
310
|
-
return deleted
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from fastapi import APIRouter
|
|
4
|
-
from starlette.websockets import WebSocket, WebSocketDisconnect
|
|
5
|
-
|
|
6
|
-
from flowent.access import authorize_websocket
|
|
7
|
-
from flowent.events import event_bus
|
|
8
|
-
|
|
9
|
-
router = APIRouter()
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@router.websocket("/ws/events")
|
|
13
|
-
async def ws_events(ws: WebSocket) -> None:
|
|
14
|
-
if not await authorize_websocket(ws):
|
|
15
|
-
return
|
|
16
|
-
await event_bus.connect_display(ws)
|
|
17
|
-
try:
|
|
18
|
-
while True:
|
|
19
|
-
await ws.receive_text()
|
|
20
|
-
except WebSocketDisconnect:
|
|
21
|
-
event_bus.disconnect_display(ws)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
@router.websocket("/ws/updates")
|
|
25
|
-
async def ws_updates(ws: WebSocket) -> None:
|
|
26
|
-
if not await authorize_websocket(ws):
|
|
27
|
-
return
|
|
28
|
-
await event_bus.connect_updates(ws)
|
|
29
|
-
try:
|
|
30
|
-
while True:
|
|
31
|
-
await ws.receive_text()
|
|
32
|
-
except WebSocketDisconnect:
|
|
33
|
-
event_bus.disconnect_updates(ws)
|