flowent 0.2.0 → 0.2.1
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/backend/pyproject.toml +31 -5
- package/backend/src/flowent/agent.py +13 -4
- package/backend/src/flowent/compact.py +35 -14
- package/backend/src/flowent/llm.py +73 -7
- package/backend/src/flowent/main.py +260 -59
- package/backend/src/flowent/static/assets/index-CRSV2xu1.css +2 -0
- package/backend/src/flowent/static/assets/index-DUYj6rgD.js +82 -0
- package/backend/src/flowent/static/index.html +2 -2
- package/backend/src/flowent/storage.py +135 -3
- package/backend/src/flowent/usage.py +315 -0
- package/backend/uv.lock +971 -3
- package/dist/frontend/assets/index-CRSV2xu1.css +2 -0
- package/dist/frontend/assets/index-DUYj6rgD.js +82 -0
- package/dist/frontend/index.html +2 -2
- package/package.json +24 -3
- package/backend/src/flowent/__pycache__/__init__.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/_version.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/agent.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/approval.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/channels.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/cli.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/compact.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__/mcp.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/mcp_import.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__/permissions.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/sandbox.cpython-313.pyc +0 -0
- package/backend/src/flowent/__pycache__/skills.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/static/assets/index-BlaCigkZ.js +0 -82
- package/backend/src/flowent/static/assets/index-CRvbsH4K.css +0 -2
- package/backend/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/__pycache__/test_agent_tools.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/__pycache__/test_approval.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/__pycache__/test_channels.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_mcp.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/__pycache__/test_patch.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/__pycache__/test_permissions.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_skills.cpython-313-pytest-9.0.3.pyc +0 -0
- package/backend/tests/__pycache__/test_startup_requirements.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/conftest.py +0 -60
- package/backend/tests/test_agent_tools.py +0 -1124
- package/backend/tests/test_approval.py +0 -283
- package/backend/tests/test_channels.py +0 -360
- package/backend/tests/test_health.py +0 -12
- package/backend/tests/test_llm_providers.py +0 -548
- package/backend/tests/test_logging.py +0 -212
- package/backend/tests/test_mcp.py +0 -788
- package/backend/tests/test_patch.py +0 -112
- package/backend/tests/test_permissions.py +0 -588
- package/backend/tests/test_persistence.py +0 -249
- package/backend/tests/test_skills.py +0 -462
- package/backend/tests/test_startup_requirements.py +0 -144
- package/backend/tests/test_workspace_chat.py +0 -2174
- package/dist/frontend/assets/index-BlaCigkZ.js +0 -82
- package/dist/frontend/assets/index-CRvbsH4K.css +0 -2
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
from fastapi.testclient import TestClient
|
|
2
|
-
|
|
3
|
-
from flowent.main import create_app
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def test_app_state_persists_providers_across_app_instances(
|
|
7
|
-
tmp_path, monkeypatch
|
|
8
|
-
) -> None:
|
|
9
|
-
monkeypatch.setenv("FLOWENT_DATA_DIR", str(tmp_path))
|
|
10
|
-
client = TestClient(create_app(serve_frontend=False))
|
|
11
|
-
|
|
12
|
-
response = client.post(
|
|
13
|
-
"/api/providers",
|
|
14
|
-
json={
|
|
15
|
-
"api_key": "sk-local",
|
|
16
|
-
"base_url": "https://api.example.test/v1",
|
|
17
|
-
"id": "provider-openai",
|
|
18
|
-
"models": ["gpt-5.1", "gpt-5.1-mini"],
|
|
19
|
-
"name": "OpenAI",
|
|
20
|
-
"type": "openai",
|
|
21
|
-
},
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
assert response.status_code == 200
|
|
25
|
-
|
|
26
|
-
restarted_client = TestClient(create_app(serve_frontend=False))
|
|
27
|
-
state_response = restarted_client.get("/api/state")
|
|
28
|
-
|
|
29
|
-
assert state_response.status_code == 200
|
|
30
|
-
assert state_response.json()["providers"] == [
|
|
31
|
-
{
|
|
32
|
-
"api_key": "sk-local",
|
|
33
|
-
"base_url": "https://api.example.test/v1",
|
|
34
|
-
"id": "provider-openai",
|
|
35
|
-
"models": ["gpt-5.1", "gpt-5.1-mini"],
|
|
36
|
-
"name": "OpenAI",
|
|
37
|
-
"type": "openai",
|
|
38
|
-
}
|
|
39
|
-
]
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def test_app_state_persists_telegram_bot_across_app_instances(
|
|
43
|
-
tmp_path, monkeypatch
|
|
44
|
-
) -> None:
|
|
45
|
-
monkeypatch.setenv("FLOWENT_DATA_DIR", str(tmp_path))
|
|
46
|
-
client = TestClient(create_app(serve_frontend=False))
|
|
47
|
-
|
|
48
|
-
response = client.put(
|
|
49
|
-
"/api/telegram-bot",
|
|
50
|
-
json={
|
|
51
|
-
"bot_token": "telegram-secret",
|
|
52
|
-
"enabled": False,
|
|
53
|
-
"sessions": [],
|
|
54
|
-
},
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
assert response.status_code == 200
|
|
58
|
-
|
|
59
|
-
restarted_client = TestClient(create_app(serve_frontend=False))
|
|
60
|
-
state_response = restarted_client.get("/api/state")
|
|
61
|
-
|
|
62
|
-
assert state_response.status_code == 200
|
|
63
|
-
assert state_response.json()["telegram_bot"] == {
|
|
64
|
-
"bot_token": "telegram-secret",
|
|
65
|
-
"enabled": False,
|
|
66
|
-
"error": "",
|
|
67
|
-
"sessions": [],
|
|
68
|
-
"status": "disabled",
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def test_app_state_persists_settings_and_workspace_messages(
|
|
73
|
-
tmp_path, monkeypatch
|
|
74
|
-
) -> None:
|
|
75
|
-
monkeypatch.setenv("FLOWENT_DATA_DIR", str(tmp_path))
|
|
76
|
-
client = TestClient(create_app(serve_frontend=False))
|
|
77
|
-
client.post(
|
|
78
|
-
"/api/providers",
|
|
79
|
-
json={
|
|
80
|
-
"api_key": "",
|
|
81
|
-
"base_url": "",
|
|
82
|
-
"id": "provider-anthropic",
|
|
83
|
-
"models": ["claude-sonnet-4-5"],
|
|
84
|
-
"name": "Anthropic",
|
|
85
|
-
"type": "anthropic",
|
|
86
|
-
},
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
settings_response = client.put(
|
|
90
|
-
"/api/settings",
|
|
91
|
-
json={
|
|
92
|
-
"agent_prompt": "Respond with careful implementation plans.",
|
|
93
|
-
"reasoning_effort": "xhigh",
|
|
94
|
-
"selected_model": "claude-sonnet-4-5",
|
|
95
|
-
"selected_provider_id": "provider-anthropic",
|
|
96
|
-
},
|
|
97
|
-
)
|
|
98
|
-
messages_response = client.put(
|
|
99
|
-
"/api/workspace/messages",
|
|
100
|
-
json={
|
|
101
|
-
"messages": [
|
|
102
|
-
{
|
|
103
|
-
"author": "assistant",
|
|
104
|
-
"content": "Draft a launch checklist",
|
|
105
|
-
"id": "message-1",
|
|
106
|
-
"thinking": "Read the request.",
|
|
107
|
-
"tools": [
|
|
108
|
-
{
|
|
109
|
-
"id": "tool-1",
|
|
110
|
-
"name": "read_file",
|
|
111
|
-
"status": "success",
|
|
112
|
-
"title": "Read notes.txt",
|
|
113
|
-
}
|
|
114
|
-
],
|
|
115
|
-
}
|
|
116
|
-
]
|
|
117
|
-
},
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
assert settings_response.status_code == 200
|
|
121
|
-
assert messages_response.status_code == 200
|
|
122
|
-
|
|
123
|
-
restarted_client = TestClient(create_app(serve_frontend=False))
|
|
124
|
-
state = restarted_client.get("/api/state").json()
|
|
125
|
-
|
|
126
|
-
assert state["settings"] == {
|
|
127
|
-
"agent_prompt": "Respond with careful implementation plans.",
|
|
128
|
-
"reasoning_effort": "xhigh",
|
|
129
|
-
"selected_model": "claude-sonnet-4-5",
|
|
130
|
-
"selected_provider_id": "provider-anthropic",
|
|
131
|
-
}
|
|
132
|
-
assert state["messages"] == [
|
|
133
|
-
{
|
|
134
|
-
"author": "assistant",
|
|
135
|
-
"content": "Draft a launch checklist",
|
|
136
|
-
"id": "message-1",
|
|
137
|
-
"thinking": "Read the request.",
|
|
138
|
-
"tools": [
|
|
139
|
-
{
|
|
140
|
-
"arguments": None,
|
|
141
|
-
"content": None,
|
|
142
|
-
"data": None,
|
|
143
|
-
"id": "tool-1",
|
|
144
|
-
"name": "read_file",
|
|
145
|
-
"status": "success",
|
|
146
|
-
"title": "Read notes.txt",
|
|
147
|
-
}
|
|
148
|
-
],
|
|
149
|
-
}
|
|
150
|
-
]
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def test_app_state_persists_workspace_error_blocks_across_app_instances(
|
|
154
|
-
tmp_path, monkeypatch
|
|
155
|
-
) -> None:
|
|
156
|
-
monkeypatch.setenv("FLOWENT_DATA_DIR", str(tmp_path))
|
|
157
|
-
client = TestClient(create_app(serve_frontend=False))
|
|
158
|
-
|
|
159
|
-
messages_response = client.put(
|
|
160
|
-
"/api/workspace/messages",
|
|
161
|
-
json={
|
|
162
|
-
"messages": [
|
|
163
|
-
{
|
|
164
|
-
"author": "assistant",
|
|
165
|
-
"content": "",
|
|
166
|
-
"groups": [
|
|
167
|
-
{
|
|
168
|
-
"id": "message-1-errors",
|
|
169
|
-
"items": [
|
|
170
|
-
{
|
|
171
|
-
"detail": "HTML response returned.",
|
|
172
|
-
"id": "message-1-error-1",
|
|
173
|
-
"message": "Check the model connection settings and try again.",
|
|
174
|
-
"title": "Request failed",
|
|
175
|
-
"type": "error",
|
|
176
|
-
}
|
|
177
|
-
],
|
|
178
|
-
}
|
|
179
|
-
],
|
|
180
|
-
"id": "message-1",
|
|
181
|
-
"status": "failed",
|
|
182
|
-
}
|
|
183
|
-
]
|
|
184
|
-
},
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
assert messages_response.status_code == 200
|
|
188
|
-
|
|
189
|
-
restarted_client = TestClient(create_app(serve_frontend=False))
|
|
190
|
-
state = restarted_client.get("/api/state").json()
|
|
191
|
-
|
|
192
|
-
assert state["messages"] == [
|
|
193
|
-
{
|
|
194
|
-
"author": "assistant",
|
|
195
|
-
"content": "",
|
|
196
|
-
"groups": [
|
|
197
|
-
{
|
|
198
|
-
"id": "message-1-errors",
|
|
199
|
-
"items": [
|
|
200
|
-
{
|
|
201
|
-
"detail": "HTML response returned.",
|
|
202
|
-
"id": "message-1-error-1",
|
|
203
|
-
"message": "Check the model connection settings and try again.",
|
|
204
|
-
"title": "Request failed",
|
|
205
|
-
"type": "error",
|
|
206
|
-
}
|
|
207
|
-
],
|
|
208
|
-
}
|
|
209
|
-
],
|
|
210
|
-
"id": "message-1",
|
|
211
|
-
"status": "failed",
|
|
212
|
-
"tools": [],
|
|
213
|
-
}
|
|
214
|
-
]
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
def test_data_directory_uses_flowent_data_dir(tmp_path, monkeypatch) -> None:
|
|
218
|
-
data_dir = tmp_path / "custom-flowent"
|
|
219
|
-
monkeypatch.setenv("FLOWENT_DATA_DIR", str(data_dir))
|
|
220
|
-
|
|
221
|
-
client = TestClient(create_app(serve_frontend=False))
|
|
222
|
-
response = client.get("/api/state")
|
|
223
|
-
|
|
224
|
-
assert response.status_code == 200
|
|
225
|
-
assert (data_dir / "flowent.db").is_file()
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
def test_app_state_defaults_reasoning_effort_for_existing_settings(
|
|
229
|
-
tmp_path, monkeypatch
|
|
230
|
-
) -> None:
|
|
231
|
-
monkeypatch.setenv("FLOWENT_DATA_DIR", str(tmp_path))
|
|
232
|
-
client = TestClient(create_app(serve_frontend=False))
|
|
233
|
-
|
|
234
|
-
response = client.get("/api/state")
|
|
235
|
-
|
|
236
|
-
assert response.status_code == 200
|
|
237
|
-
assert response.json()["settings"]["reasoning_effort"] == "default"
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
def test_app_state_defaults_agent_prompt_for_existing_settings(
|
|
241
|
-
tmp_path, monkeypatch
|
|
242
|
-
) -> None:
|
|
243
|
-
monkeypatch.setenv("FLOWENT_DATA_DIR", str(tmp_path))
|
|
244
|
-
client = TestClient(create_app(serve_frontend=False))
|
|
245
|
-
|
|
246
|
-
response = client.get("/api/state")
|
|
247
|
-
|
|
248
|
-
assert response.status_code == 200
|
|
249
|
-
assert response.json()["settings"].get("agent_prompt", "") == ""
|