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,182 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from fastapi.testclient import TestClient
|
|
3
|
-
from starlette.websockets import WebSocketDisconnect
|
|
4
|
-
|
|
5
|
-
from flowent.main import create_app
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def _create_client(monkeypatch, tmp_path, *, configured: bool) -> TestClient:
|
|
9
|
-
import flowent.settings as settings_module
|
|
10
|
-
from flowent.access import set_access_code
|
|
11
|
-
|
|
12
|
-
settings_file = tmp_path / "settings.json"
|
|
13
|
-
monkeypatch.setattr(settings_module, "_SETTINGS_FILE", settings_file)
|
|
14
|
-
monkeypatch.setattr(settings_module, "_cached_settings", None)
|
|
15
|
-
|
|
16
|
-
settings = settings_module.Settings()
|
|
17
|
-
if configured:
|
|
18
|
-
set_access_code(settings, "TEST-ACCESS-CODE")
|
|
19
|
-
settings_module.save_settings(settings)
|
|
20
|
-
monkeypatch.setattr(settings_module, "_cached_settings", None)
|
|
21
|
-
|
|
22
|
-
return TestClient(create_app())
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def test_protected_api_requires_admin_session(monkeypatch, tmp_path):
|
|
26
|
-
with _create_client(monkeypatch, tmp_path, configured=True) as client:
|
|
27
|
-
access_state = client.get("/api/access/state")
|
|
28
|
-
protected_response = client.get("/api/settings/bootstrap")
|
|
29
|
-
|
|
30
|
-
assert access_state.status_code == 200
|
|
31
|
-
assert access_state.json() == {
|
|
32
|
-
"authenticated": False,
|
|
33
|
-
"configured": True,
|
|
34
|
-
"bootstrap_generated": False,
|
|
35
|
-
"requires_restart": False,
|
|
36
|
-
}
|
|
37
|
-
assert protected_response.status_code == 401
|
|
38
|
-
assert protected_response.json() == {"detail": "Access denied"}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def test_access_login_and_logout_flow(monkeypatch, tmp_path):
|
|
42
|
-
with _create_client(monkeypatch, tmp_path, configured=True) as client:
|
|
43
|
-
login_response = client.post(
|
|
44
|
-
"/api/access/login",
|
|
45
|
-
json={"code": "TEST-ACCESS-CODE"},
|
|
46
|
-
)
|
|
47
|
-
protected_response = client.get("/api/settings/bootstrap")
|
|
48
|
-
logout_response = client.post("/api/access/logout")
|
|
49
|
-
denied_response = client.get("/api/settings/bootstrap")
|
|
50
|
-
|
|
51
|
-
assert login_response.status_code == 200
|
|
52
|
-
assert login_response.json()["authenticated"] is True
|
|
53
|
-
assert protected_response.status_code == 200
|
|
54
|
-
assert logout_response.status_code == 200
|
|
55
|
-
assert logout_response.json()["authenticated"] is False
|
|
56
|
-
assert denied_response.status_code == 401
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def test_admin_session_survives_backend_restart(monkeypatch, tmp_path):
|
|
60
|
-
import flowent.settings as settings_module
|
|
61
|
-
|
|
62
|
-
with _create_client(monkeypatch, tmp_path, configured=True) as client:
|
|
63
|
-
login_response = client.post(
|
|
64
|
-
"/api/access/login",
|
|
65
|
-
json={"code": "TEST-ACCESS-CODE"},
|
|
66
|
-
)
|
|
67
|
-
session_cookie = client.cookies.get("flowent_admin_session")
|
|
68
|
-
|
|
69
|
-
assert login_response.status_code == 200
|
|
70
|
-
assert session_cookie
|
|
71
|
-
persisted_settings, _ = settings_module._read_settings_file()
|
|
72
|
-
assert persisted_settings.access.code == "TEST-ACCESS-CODE"
|
|
73
|
-
assert persisted_settings.access.session_signing_secret
|
|
74
|
-
|
|
75
|
-
with _create_client(monkeypatch, tmp_path, configured=True) as restarted_client:
|
|
76
|
-
restarted_client.cookies.set("flowent_admin_session", session_cookie)
|
|
77
|
-
access_state = restarted_client.get("/api/access/state")
|
|
78
|
-
protected_response = restarted_client.get("/api/settings/bootstrap")
|
|
79
|
-
|
|
80
|
-
assert access_state.status_code == 200
|
|
81
|
-
assert access_state.json()["authenticated"] is True
|
|
82
|
-
assert protected_response.status_code == 200
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def test_access_code_rotation_invalidates_existing_admin_session(
|
|
86
|
-
monkeypatch,
|
|
87
|
-
tmp_path,
|
|
88
|
-
):
|
|
89
|
-
import flowent.settings as settings_module
|
|
90
|
-
from flowent.access import set_access_code
|
|
91
|
-
|
|
92
|
-
with _create_client(monkeypatch, tmp_path, configured=True) as client:
|
|
93
|
-
login_response = client.post(
|
|
94
|
-
"/api/access/login",
|
|
95
|
-
json={"code": "TEST-ACCESS-CODE"},
|
|
96
|
-
)
|
|
97
|
-
session_cookie = client.cookies.get("flowent_admin_session")
|
|
98
|
-
|
|
99
|
-
assert login_response.status_code == 200
|
|
100
|
-
assert session_cookie
|
|
101
|
-
|
|
102
|
-
rotated_settings = settings_module.get_settings()
|
|
103
|
-
set_access_code(rotated_settings, "NEW-ACCESS-CODE")
|
|
104
|
-
settings_module.save_settings(rotated_settings)
|
|
105
|
-
monkeypatch.setattr(settings_module, "_cached_settings", None)
|
|
106
|
-
|
|
107
|
-
with TestClient(create_app()) as restarted_client:
|
|
108
|
-
restarted_client.cookies.set("flowent_admin_session", session_cookie)
|
|
109
|
-
access_state = restarted_client.get("/api/access/state")
|
|
110
|
-
protected_response = restarted_client.get("/api/settings/bootstrap")
|
|
111
|
-
|
|
112
|
-
assert access_state.status_code == 200
|
|
113
|
-
assert access_state.json()["authenticated"] is False
|
|
114
|
-
assert protected_response.status_code == 401
|
|
115
|
-
assert protected_response.json() == {"detail": "Access denied"}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def test_websocket_requires_admin_session(monkeypatch, tmp_path):
|
|
119
|
-
with _create_client(monkeypatch, tmp_path, configured=True) as client:
|
|
120
|
-
with pytest.raises(WebSocketDisconnect), client.websocket_connect("/ws/events"):
|
|
121
|
-
pass
|
|
122
|
-
|
|
123
|
-
login_response = client.post(
|
|
124
|
-
"/api/access/login",
|
|
125
|
-
json={"code": "TEST-ACCESS-CODE"},
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
with client.websocket_connect("/ws/events"):
|
|
129
|
-
pass
|
|
130
|
-
|
|
131
|
-
assert login_response.status_code == 200
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def test_access_state_reports_bootstrap_generated_code(monkeypatch, tmp_path):
|
|
135
|
-
with _create_client(monkeypatch, tmp_path, configured=False) as client:
|
|
136
|
-
access_state = client.get("/api/access/state")
|
|
137
|
-
|
|
138
|
-
assert access_state.status_code == 200
|
|
139
|
-
assert access_state.json() == {
|
|
140
|
-
"authenticated": False,
|
|
141
|
-
"configured": True,
|
|
142
|
-
"bootstrap_generated": True,
|
|
143
|
-
"requires_restart": False,
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
def test_legacy_hashed_only_access_rotates_to_persisted_code_at_startup(
|
|
148
|
-
monkeypatch,
|
|
149
|
-
tmp_path,
|
|
150
|
-
):
|
|
151
|
-
import flowent.settings as settings_module
|
|
152
|
-
from flowent.access import set_access_code, verify_access_code
|
|
153
|
-
|
|
154
|
-
settings_file = tmp_path / "settings.json"
|
|
155
|
-
monkeypatch.setattr(settings_module, "_SETTINGS_FILE", settings_file)
|
|
156
|
-
monkeypatch.setattr(settings_module, "_cached_settings", None)
|
|
157
|
-
|
|
158
|
-
settings = settings_module.Settings()
|
|
159
|
-
set_access_code(settings, "OLD-ACCESS-CODE")
|
|
160
|
-
settings.access.code = ""
|
|
161
|
-
settings_module.save_settings(settings)
|
|
162
|
-
monkeypatch.setattr(settings_module, "_cached_settings", None)
|
|
163
|
-
|
|
164
|
-
with TestClient(create_app()) as client:
|
|
165
|
-
access_state = client.get("/api/access/state")
|
|
166
|
-
|
|
167
|
-
assert access_state.status_code == 200
|
|
168
|
-
assert access_state.json() == {
|
|
169
|
-
"authenticated": False,
|
|
170
|
-
"configured": True,
|
|
171
|
-
"bootstrap_generated": True,
|
|
172
|
-
"requires_restart": False,
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
persisted_settings = settings_module.load_settings()
|
|
176
|
-
assert persisted_settings.access.code
|
|
177
|
-
assert persisted_settings.access.code != "OLD-ACCESS-CODE"
|
|
178
|
-
assert verify_access_code(
|
|
179
|
-
persisted_settings.access,
|
|
180
|
-
persisted_settings.access.code,
|
|
181
|
-
)
|
|
182
|
-
assert not verify_access_code(persisted_settings.access, "OLD-ACCESS-CODE")
|
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
import base64
|
|
2
|
-
from uuid import UUID
|
|
3
|
-
|
|
4
|
-
from flowent.models import (
|
|
5
|
-
AssistantText,
|
|
6
|
-
ImagePart,
|
|
7
|
-
LLMResponse,
|
|
8
|
-
ReceivedMessage,
|
|
9
|
-
TextPart,
|
|
10
|
-
)
|
|
11
|
-
from flowent.registry import registry
|
|
12
|
-
|
|
13
|
-
_ONE_PIXEL_PNG = base64.b64decode(
|
|
14
|
-
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+aF9sAAAAASUVORK5CYII="
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def _get_assistant_id(client) -> str:
|
|
19
|
-
response = client.get("/api/assistant")
|
|
20
|
-
|
|
21
|
-
assert response.status_code == 200
|
|
22
|
-
assistant_id = response.json()["id"]
|
|
23
|
-
UUID(assistant_id)
|
|
24
|
-
return assistant_id
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def test_help_command_returns_visible_command_feedback(client):
|
|
28
|
-
assistant_id = _get_assistant_id(client)
|
|
29
|
-
|
|
30
|
-
response = client.post("/api/assistant/message", json={"content": "/help"})
|
|
31
|
-
|
|
32
|
-
assert response.status_code == 200
|
|
33
|
-
assert response.json() == {
|
|
34
|
-
"status": "command_executed",
|
|
35
|
-
"command_name": "/help",
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
detail = client.get(f"/api/nodes/{assistant_id}").json()
|
|
39
|
-
|
|
40
|
-
assert any(
|
|
41
|
-
entry["type"] == "CommandResultEntry"
|
|
42
|
-
and entry["command_name"] == "/help"
|
|
43
|
-
and "/compact" in entry["content"]
|
|
44
|
-
for entry in detail["history"]
|
|
45
|
-
)
|
|
46
|
-
assert not any(
|
|
47
|
-
entry["type"] == "ReceivedMessage" and entry.get("content") == "/help"
|
|
48
|
-
for entry in detail["history"]
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def test_clear_command_clears_history_back_to_empty_state(client):
|
|
53
|
-
assistant_id = _get_assistant_id(client)
|
|
54
|
-
assistant = registry.get(assistant_id)
|
|
55
|
-
assert assistant is not None
|
|
56
|
-
assistant.history.append(ReceivedMessage(content="Old message", from_id="human"))
|
|
57
|
-
assistant.history.append(AssistantText(content="Old reply"))
|
|
58
|
-
|
|
59
|
-
response = client.post("/api/assistant/message", json={"content": "/clear"})
|
|
60
|
-
|
|
61
|
-
assert response.status_code == 200
|
|
62
|
-
assert response.json() == {
|
|
63
|
-
"status": "command_executed",
|
|
64
|
-
"command_name": "/clear",
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
detail = client.get(f"/api/nodes/{assistant_id}").json()
|
|
68
|
-
|
|
69
|
-
assert not any(
|
|
70
|
-
entry["type"] in {"ReceivedMessage", "AssistantText"}
|
|
71
|
-
and entry.get("content") in {"Old message", "Old reply"}
|
|
72
|
-
for entry in detail["history"]
|
|
73
|
-
)
|
|
74
|
-
assert not any(
|
|
75
|
-
entry["type"] == "CommandResultEntry" and entry["command_name"] == "/clear"
|
|
76
|
-
for entry in detail["history"]
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def test_compact_command_replaces_history_with_summary(monkeypatch, client):
|
|
81
|
-
assistant_id = _get_assistant_id(client)
|
|
82
|
-
assistant = registry.get(assistant_id)
|
|
83
|
-
assert assistant is not None
|
|
84
|
-
assistant.history.extend(
|
|
85
|
-
[
|
|
86
|
-
ReceivedMessage(content="Need a concise recap", from_id="human"),
|
|
87
|
-
AssistantText(content="I will summarize the open work."),
|
|
88
|
-
]
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
monkeypatch.setattr(
|
|
92
|
-
"flowent.agent.gateway.chat",
|
|
93
|
-
lambda *args, **kwargs: LLMResponse(
|
|
94
|
-
content=(
|
|
95
|
-
"## Current Goal\nShip the slash commands.\n\n"
|
|
96
|
-
"## Active Task Boundary\nKeep the fix limited to Assistant chat.\n\n"
|
|
97
|
-
"## Key Constraints\nDo not lose persisted context.\n\n"
|
|
98
|
-
"## Confirmed Decisions\nUse built-in commands only.\n\n"
|
|
99
|
-
"## Open Questions\nNone.\n\n"
|
|
100
|
-
"## Next Actions\nWire the UI and tests."
|
|
101
|
-
)
|
|
102
|
-
),
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
response = client.post(
|
|
106
|
-
"/api/assistant/message",
|
|
107
|
-
json={"content": "/compact slash command rollout"},
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
assert response.status_code == 200
|
|
111
|
-
assert response.json() == {
|
|
112
|
-
"status": "command_executed",
|
|
113
|
-
"command_name": "/compact",
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
detail = client.get(f"/api/nodes/{assistant_id}").json()
|
|
117
|
-
|
|
118
|
-
assert any(
|
|
119
|
-
entry["type"] == "ReceivedMessage"
|
|
120
|
-
and entry.get("content") == "Need a concise recap"
|
|
121
|
-
for entry in detail["history"]
|
|
122
|
-
)
|
|
123
|
-
assert any(
|
|
124
|
-
entry["type"] == "AssistantText"
|
|
125
|
-
and entry.get("content") == "I will summarize the open work."
|
|
126
|
-
for entry in detail["history"]
|
|
127
|
-
)
|
|
128
|
-
assert any(
|
|
129
|
-
entry["type"] == "CommandResultEntry"
|
|
130
|
-
and entry["command_name"] == "/compact"
|
|
131
|
-
and entry.get("include_in_context") is False
|
|
132
|
-
and "Compacted this chat for future replies." in entry["content"]
|
|
133
|
-
and "Focus: slash command rollout" in entry["content"]
|
|
134
|
-
and "Ship the slash commands." not in entry["content"]
|
|
135
|
-
for entry in detail["history"]
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def test_retry_message_rewrites_tail_and_reuses_image_parts(monkeypatch, client):
|
|
140
|
-
assistant_id = _get_assistant_id(client)
|
|
141
|
-
assistant = registry.get(assistant_id)
|
|
142
|
-
assert assistant is not None
|
|
143
|
-
queued_messages = []
|
|
144
|
-
|
|
145
|
-
upload_response = client.post(
|
|
146
|
-
"/api/image-assets",
|
|
147
|
-
files={"file": ("pixel.png", _ONE_PIXEL_PNG, "image/png")},
|
|
148
|
-
)
|
|
149
|
-
assert upload_response.status_code == 200
|
|
150
|
-
asset_id = upload_response.json()["id"]
|
|
151
|
-
|
|
152
|
-
assistant.history.extend(
|
|
153
|
-
[
|
|
154
|
-
ReceivedMessage(
|
|
155
|
-
content="Keep this turn",
|
|
156
|
-
from_id="human",
|
|
157
|
-
message_id="msg-1",
|
|
158
|
-
),
|
|
159
|
-
AssistantText(content="Keep this reply"),
|
|
160
|
-
ReceivedMessage(
|
|
161
|
-
from_id="human",
|
|
162
|
-
parts=[
|
|
163
|
-
TextPart(text="Retry this image request"),
|
|
164
|
-
ImagePart(
|
|
165
|
-
asset_id=asset_id,
|
|
166
|
-
mime_type="image/png",
|
|
167
|
-
width=1,
|
|
168
|
-
height=1,
|
|
169
|
-
),
|
|
170
|
-
],
|
|
171
|
-
message_id="msg-2",
|
|
172
|
-
),
|
|
173
|
-
AssistantText(content="Discard this reply"),
|
|
174
|
-
]
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
monkeypatch.setattr(assistant, "supports_input_image", lambda: True)
|
|
178
|
-
monkeypatch.setattr(
|
|
179
|
-
assistant,
|
|
180
|
-
"enqueue_message",
|
|
181
|
-
lambda message: queued_messages.append(message),
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
response = client.post("/api/assistant/messages/msg-2/retry")
|
|
185
|
-
|
|
186
|
-
assert response.status_code == 200
|
|
187
|
-
payload = response.json()
|
|
188
|
-
assert payload["status"] == "retried"
|
|
189
|
-
assert payload["message_id"] != "msg-2"
|
|
190
|
-
|
|
191
|
-
detail = client.get(f"/api/nodes/{assistant_id}").json()
|
|
192
|
-
|
|
193
|
-
assert any(
|
|
194
|
-
entry["type"] == "ReceivedMessage"
|
|
195
|
-
and entry.get("message_id") == "msg-1"
|
|
196
|
-
and entry.get("content") == "Keep this turn"
|
|
197
|
-
for entry in detail["history"]
|
|
198
|
-
)
|
|
199
|
-
assert not any(
|
|
200
|
-
entry["type"] == "ReceivedMessage" and entry.get("message_id") == "msg-2"
|
|
201
|
-
for entry in detail["history"]
|
|
202
|
-
)
|
|
203
|
-
assert not any(
|
|
204
|
-
entry["type"] == "AssistantText"
|
|
205
|
-
and entry.get("content") == "Discard this reply"
|
|
206
|
-
for entry in detail["history"]
|
|
207
|
-
)
|
|
208
|
-
assert any(
|
|
209
|
-
entry["type"] == "ReceivedMessage"
|
|
210
|
-
and entry.get("message_id") == payload["message_id"]
|
|
211
|
-
and entry.get("content") == "Retry this image request[image]"
|
|
212
|
-
for entry in detail["history"]
|
|
213
|
-
)
|
|
214
|
-
assert len(queued_messages) == 1
|
|
215
|
-
assert queued_messages[0].message_id == payload["message_id"]
|
|
216
|
-
assert queued_messages[0].parts[0].text == "Retry this image request"
|
|
217
|
-
assert queued_messages[0].parts[1].asset_id == asset_id
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
def test_retry_message_returns_404_for_missing_anchor(client):
|
|
221
|
-
response = client.post("/api/assistant/messages/missing/retry")
|
|
222
|
-
|
|
223
|
-
assert response.status_code == 404
|
|
224
|
-
assert "was not found" in response.json()["detail"]
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
def test_retry_message_returns_409_when_image_input_is_unsupported(monkeypatch, client):
|
|
228
|
-
assistant_id = _get_assistant_id(client)
|
|
229
|
-
assistant = registry.get(assistant_id)
|
|
230
|
-
assert assistant is not None
|
|
231
|
-
|
|
232
|
-
upload_response = client.post(
|
|
233
|
-
"/api/image-assets",
|
|
234
|
-
files={"file": ("pixel.png", _ONE_PIXEL_PNG, "image/png")},
|
|
235
|
-
)
|
|
236
|
-
assert upload_response.status_code == 200
|
|
237
|
-
asset_id = upload_response.json()["id"]
|
|
238
|
-
|
|
239
|
-
assistant.history.append(
|
|
240
|
-
ReceivedMessage(
|
|
241
|
-
from_id="human",
|
|
242
|
-
parts=[
|
|
243
|
-
ImagePart(asset_id=asset_id, mime_type="image/png", width=1, height=1)
|
|
244
|
-
],
|
|
245
|
-
message_id="msg-image",
|
|
246
|
-
)
|
|
247
|
-
)
|
|
248
|
-
monkeypatch.setattr(assistant, "supports_input_image", lambda: False)
|
|
249
|
-
|
|
250
|
-
response = client.post("/api/assistant/messages/msg-image/retry")
|
|
251
|
-
|
|
252
|
-
assert response.status_code == 409
|
|
253
|
-
assert response.json()["detail"] == (
|
|
254
|
-
"Assistant current model does not support `input_image`."
|
|
255
|
-
)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
def test_upload_image_asset_returns_metadata_and_serves_bytes(client):
|
|
259
|
-
response = client.post(
|
|
260
|
-
"/api/image-assets",
|
|
261
|
-
files={"file": ("pixel.png", _ONE_PIXEL_PNG, "image/png")},
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
assert response.status_code == 200
|
|
265
|
-
payload = response.json()
|
|
266
|
-
assert payload["mime_type"] == "image/png"
|
|
267
|
-
assert payload["width"] == 1
|
|
268
|
-
assert payload["height"] == 1
|
|
269
|
-
|
|
270
|
-
image_response = client.get(f"/api/image-assets/{payload['id']}")
|
|
271
|
-
|
|
272
|
-
assert image_response.status_code == 200
|
|
273
|
-
assert image_response.headers["content-type"].startswith("image/png")
|
|
274
|
-
assert image_response.content == _ONE_PIXEL_PNG
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
def test_image_message_bypasses_assistant_commands(monkeypatch, client):
|
|
278
|
-
assistant_id = _get_assistant_id(client)
|
|
279
|
-
assistant = registry.get(assistant_id)
|
|
280
|
-
assert assistant is not None
|
|
281
|
-
queued_messages = []
|
|
282
|
-
|
|
283
|
-
upload_response = client.post(
|
|
284
|
-
"/api/image-assets",
|
|
285
|
-
files={"file": ("pixel.png", _ONE_PIXEL_PNG, "image/png")},
|
|
286
|
-
)
|
|
287
|
-
assert upload_response.status_code == 200
|
|
288
|
-
asset_id = upload_response.json()["id"]
|
|
289
|
-
|
|
290
|
-
monkeypatch.setattr(assistant, "supports_input_image", lambda: True)
|
|
291
|
-
monkeypatch.setattr(
|
|
292
|
-
assistant, "enqueue_message", lambda message: queued_messages.append(message)
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
response = client.post(
|
|
296
|
-
"/api/assistant/message",
|
|
297
|
-
json={
|
|
298
|
-
"parts": [
|
|
299
|
-
{"type": "text", "text": "/help"},
|
|
300
|
-
{
|
|
301
|
-
"type": "image",
|
|
302
|
-
"asset_id": asset_id,
|
|
303
|
-
"mime_type": "image/png",
|
|
304
|
-
"width": 1,
|
|
305
|
-
"height": 1,
|
|
306
|
-
"alt": "Pixel",
|
|
307
|
-
},
|
|
308
|
-
]
|
|
309
|
-
},
|
|
310
|
-
)
|
|
311
|
-
|
|
312
|
-
assert response.status_code == 200
|
|
313
|
-
assert response.json()["status"] == "sent"
|
|
314
|
-
assert len(queued_messages) == 1
|
|
315
|
-
assert queued_messages[0].parts[0].text == "/help"
|
|
316
|
-
assert queued_messages[0].parts[1].asset_id == asset_id
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
def test_image_message_is_rejected_when_assistant_lacks_input_image_support(
|
|
320
|
-
monkeypatch, client
|
|
321
|
-
):
|
|
322
|
-
assistant_id = _get_assistant_id(client)
|
|
323
|
-
assistant = registry.get(assistant_id)
|
|
324
|
-
assert assistant is not None
|
|
325
|
-
|
|
326
|
-
upload_response = client.post(
|
|
327
|
-
"/api/image-assets",
|
|
328
|
-
files={"file": ("pixel.png", _ONE_PIXEL_PNG, "image/png")},
|
|
329
|
-
)
|
|
330
|
-
assert upload_response.status_code == 200
|
|
331
|
-
asset_id = upload_response.json()["id"]
|
|
332
|
-
|
|
333
|
-
monkeypatch.setattr(assistant, "supports_input_image", lambda: False)
|
|
334
|
-
|
|
335
|
-
response = client.post(
|
|
336
|
-
"/api/assistant/message",
|
|
337
|
-
json={
|
|
338
|
-
"parts": [
|
|
339
|
-
{
|
|
340
|
-
"type": "image",
|
|
341
|
-
"asset_id": asset_id,
|
|
342
|
-
"mime_type": "image/png",
|
|
343
|
-
"width": 1,
|
|
344
|
-
"height": 1,
|
|
345
|
-
}
|
|
346
|
-
]
|
|
347
|
-
},
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
assert response.status_code == 409
|
|
351
|
-
assert (
|
|
352
|
-
response.json()["detail"]
|
|
353
|
-
== "Assistant current model does not support `input_image`."
|
|
354
|
-
)
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
|
|
3
|
-
from fastapi.testclient import TestClient
|
|
4
|
-
|
|
5
|
-
from flowent.main import create_app
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def _create_client(monkeypatch, tmp_path, *, serve_frontend: bool) -> TestClient:
|
|
9
|
-
import flowent.settings as settings_module
|
|
10
|
-
|
|
11
|
-
settings_file = tmp_path / "settings.json"
|
|
12
|
-
static_dir = tmp_path / "static"
|
|
13
|
-
assets_dir = static_dir / "assets"
|
|
14
|
-
|
|
15
|
-
assets_dir.mkdir(parents=True)
|
|
16
|
-
(static_dir / "index.html").write_text(
|
|
17
|
-
"<!doctype html><html><body>Flowent</body></html>",
|
|
18
|
-
encoding="utf-8",
|
|
19
|
-
)
|
|
20
|
-
(assets_dir / "app.js").write_text("console.log('flowent')", encoding="utf-8")
|
|
21
|
-
settings_file.write_text(
|
|
22
|
-
json.dumps(
|
|
23
|
-
{
|
|
24
|
-
"event_log": {"timestamp_format": "absolute"},
|
|
25
|
-
"model": {"active_provider_id": "", "active_model": ""},
|
|
26
|
-
"custom_prompt": "",
|
|
27
|
-
"custom_post_prompt": "",
|
|
28
|
-
"providers": [],
|
|
29
|
-
"roles": [],
|
|
30
|
-
}
|
|
31
|
-
),
|
|
32
|
-
encoding="utf-8",
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
monkeypatch.setattr(settings_module, "_SETTINGS_FILE", settings_file)
|
|
36
|
-
monkeypatch.setattr(settings_module, "_cached_settings", None)
|
|
37
|
-
monkeypatch.setenv("FLOWENT_STATIC_DIR", str(static_dir))
|
|
38
|
-
|
|
39
|
-
return TestClient(create_app(serve_frontend=serve_frontend))
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def test_production_app_serves_frontend_index(monkeypatch, tmp_path):
|
|
43
|
-
with _create_client(monkeypatch, tmp_path, serve_frontend=True) as client:
|
|
44
|
-
response = client.get("/")
|
|
45
|
-
nested_response = client.get("/workspace")
|
|
46
|
-
asset_response = client.get("/assets/app.js")
|
|
47
|
-
|
|
48
|
-
assert response.status_code == 200
|
|
49
|
-
assert response.headers["content-type"].startswith("text/html")
|
|
50
|
-
assert nested_response.status_code == 200
|
|
51
|
-
assert nested_response.headers["content-type"].startswith("text/html")
|
|
52
|
-
assert asset_response.status_code == 200
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def test_dev_app_does_not_serve_frontend_routes(monkeypatch, tmp_path):
|
|
56
|
-
with _create_client(monkeypatch, tmp_path, serve_frontend=False) as client:
|
|
57
|
-
root_response = client.get("/")
|
|
58
|
-
page_response = client.get("/workspace")
|
|
59
|
-
|
|
60
|
-
assert root_response.status_code == 404
|
|
61
|
-
assert page_response.status_code == 404
|