flowent 0.1.3 → 0.1.5
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 +1 -1
- 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/agent.py +23 -1
- package/backend/src/flowent/approval.py +148 -0
- package/backend/src/flowent/cli.py +16 -2
- package/backend/src/flowent/compact.py +183 -0
- package/backend/src/flowent/context.py +19 -1
- package/backend/src/flowent/llm.py +51 -11
- package/backend/src/flowent/logging.py +60 -0
- package/backend/src/flowent/main.py +696 -192
- package/backend/src/flowent/mcp.py +3 -1
- package/backend/src/flowent/patch.py +55 -31
- package/backend/src/flowent/paths.py +12 -0
- package/backend/src/flowent/permissions.py +185 -42
- package/backend/src/flowent/sandbox.py +146 -13
- package/backend/src/flowent/static/assets/index-Cl20cARb.css +2 -0
- package/backend/src/flowent/static/assets/index-dsDDsEym.js +81 -0
- package/backend/src/flowent/static/index.html +2 -2
- package/backend/src/flowent/storage.py +257 -9
- 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/test_agent_tools.py +312 -1
- package/backend/tests/test_approval.py +283 -0
- package/backend/tests/test_llm_providers.py +216 -0
- package/backend/tests/test_logging.py +30 -0
- package/backend/tests/test_mcp.py +76 -10
- package/backend/tests/test_patch.py +112 -0
- package/backend/tests/test_permissions.py +198 -53
- package/backend/tests/test_persistence.py +78 -0
- package/backend/tests/test_startup_requirements.py +96 -0
- package/backend/tests/test_workspace_chat.py +1265 -144
- package/backend/uv.lock +1 -1
- package/dist/frontend/assets/index-Cl20cARb.css +2 -0
- package/dist/frontend/assets/index-dsDDsEym.js +81 -0
- package/dist/frontend/index.html +2 -2
- package/package.json +2 -2
- package/backend/src/flowent/static/assets/index-DjF2KBwE.js +0 -81
- package/backend/src/flowent/static/assets/index-P-bBpJG8.css +0 -2
- package/dist/frontend/assets/index-DjF2KBwE.js +0 -81
- package/dist/frontend/assets/index-P-bBpJG8.css +0 -2
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
import logging
|
|
4
5
|
import os
|
|
5
6
|
import re
|
|
6
7
|
import sys
|
|
7
8
|
from datetime import datetime
|
|
8
9
|
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
9
11
|
|
|
10
12
|
from flowent.paths import data_directory
|
|
11
13
|
|
|
@@ -14,6 +16,7 @@ DEFAULT_LOG_RETENTION = 5
|
|
|
14
16
|
LITELLM_LOGGER_NAMES = ("LiteLLM", "LiteLLM Router", "LiteLLM Proxy")
|
|
15
17
|
_configured_log_file: Path | None = None
|
|
16
18
|
_configured_log_process_id: int | None = None
|
|
19
|
+
_llm_request_counter = 0
|
|
17
20
|
_SECRET_PATTERNS = (
|
|
18
21
|
re.compile(r"(?i)\b(bearer)\s+([^\s,}]+)"),
|
|
19
22
|
re.compile(
|
|
@@ -36,10 +39,21 @@ def redact_log_value(value: object) -> str:
|
|
|
36
39
|
return text
|
|
37
40
|
|
|
38
41
|
|
|
42
|
+
def redact_diagnostic_value(value: object) -> str:
|
|
43
|
+
text = str(value)
|
|
44
|
+
for pattern in _SECRET_PATTERNS:
|
|
45
|
+
text = pattern.sub("[REDACTED]", text)
|
|
46
|
+
return text
|
|
47
|
+
|
|
48
|
+
|
|
39
49
|
def log_directory(directory: Path | None = None) -> Path:
|
|
40
50
|
return (directory or data_directory()) / "logs"
|
|
41
51
|
|
|
42
52
|
|
|
53
|
+
def llm_request_log_directory(directory: Path | None = None) -> Path:
|
|
54
|
+
return log_directory(directory) / "llm-requests"
|
|
55
|
+
|
|
56
|
+
|
|
43
57
|
def parse_log_level(value: str | None, default: int) -> int:
|
|
44
58
|
if not value:
|
|
45
59
|
return default
|
|
@@ -112,6 +126,52 @@ def new_log_file_path(directory: Path | None = None) -> Path:
|
|
|
112
126
|
return logs / f"flowent-{timestamp}-{os.getpid()}.log"
|
|
113
127
|
|
|
114
128
|
|
|
129
|
+
def sanitize_diagnostic_value(value: Any) -> Any:
|
|
130
|
+
if isinstance(value, dict):
|
|
131
|
+
return {
|
|
132
|
+
key: sanitize_diagnostic_value(item)
|
|
133
|
+
for key, item in value.items()
|
|
134
|
+
if not secret_field_name(str(key))
|
|
135
|
+
}
|
|
136
|
+
if isinstance(value, list | tuple):
|
|
137
|
+
return [sanitize_diagnostic_value(item) for item in value]
|
|
138
|
+
if isinstance(value, str):
|
|
139
|
+
return redact_diagnostic_value(value)
|
|
140
|
+
return value
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def secret_field_name(name: str) -> bool:
|
|
144
|
+
normalized = re.sub(r"[^a-z0-9]", "", name.lower())
|
|
145
|
+
return "secret" in normalized or normalized in {
|
|
146
|
+
"accesstoken",
|
|
147
|
+
"apikey",
|
|
148
|
+
"authorization",
|
|
149
|
+
"password",
|
|
150
|
+
"refreshtoken",
|
|
151
|
+
"token",
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def write_llm_request_diagnostic(payload: dict[str, Any]) -> Path | None:
|
|
156
|
+
global _llm_request_counter
|
|
157
|
+
|
|
158
|
+
if not development_mode():
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
_llm_request_counter += 1
|
|
162
|
+
timestamp = datetime.now().strftime("%Y%m%d-%H%M%S-%f")
|
|
163
|
+
path = llm_request_log_directory() / (
|
|
164
|
+
f"llm-request-{timestamp}-{os.getpid()}-{_llm_request_counter:06d}.json"
|
|
165
|
+
)
|
|
166
|
+
path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
|
|
167
|
+
path.write_text(
|
|
168
|
+
json.dumps(sanitize_diagnostic_value(payload), ensure_ascii=False, indent=2)
|
|
169
|
+
+ "\n",
|
|
170
|
+
encoding="utf-8",
|
|
171
|
+
)
|
|
172
|
+
return path
|
|
173
|
+
|
|
174
|
+
|
|
115
175
|
def prune_old_logs(logs: Path, *, keep: int = DEFAULT_LOG_RETENTION) -> None:
|
|
116
176
|
files = sorted(logs.glob("flowent-*.log"), key=lambda item: item.name)
|
|
117
177
|
for old_log in files[:-keep]:
|