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