flowent 0.2.3 → 0.3.0
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 +3 -3
- package/backend/README.md +3 -3
- package/backend/pyproject.toml +1 -1
- package/backend/src/flowent/agent.py +1 -1
- package/backend/src/flowent/api_models.py +103 -0
- package/backend/src/flowent/app.py +151 -0
- package/backend/src/flowent/cli.py +13 -4
- package/backend/src/flowent/compact.py +34 -13
- package/backend/src/flowent/llm.py +6 -8
- package/backend/src/flowent/logging.py +7 -1
- package/backend/src/flowent/main.py +18 -1989
- package/backend/src/flowent/mcp.py +231 -44
- package/backend/src/flowent/network.py +5 -0
- package/backend/src/flowent/permissions.py +5 -1
- package/backend/src/flowent/provider_connections.py +42 -0
- package/backend/src/flowent/routes/__init__.py +0 -0
- package/backend/src/flowent/routes/integrations.py +105 -0
- package/backend/src/flowent/routes/permissions.py +36 -0
- package/backend/src/flowent/routes/providers.py +30 -0
- package/backend/src/flowent/routes/system.py +49 -0
- package/backend/src/flowent/routes/workflow_routes.py +63 -0
- package/backend/src/flowent/routes/workspace.py +105 -0
- package/backend/src/flowent/sandbox.py +1 -1
- package/backend/src/flowent/state/__init__.py +53 -0
- package/backend/src/flowent/state/models.py +257 -0
- package/backend/src/flowent/state/schema.py +186 -0
- package/backend/src/flowent/state/store.py +1013 -0
- package/backend/src/flowent/static/assets/index-CvWZZMtK.css +2 -0
- package/backend/src/flowent/static/assets/index-ma2v8oW7.js +90 -0
- package/backend/src/flowent/static/index.html +2 -2
- package/backend/src/flowent/storage.py +52 -1254
- package/backend/src/flowent/system_tools.py +25 -0
- package/backend/src/flowent/tools.py +4 -2
- package/backend/src/flowent/usage.py +9 -4
- package/backend/src/flowent/workflows.py +282 -0
- package/backend/src/flowent/workspace/__init__.py +0 -0
- package/backend/src/flowent/workspace/context.py +249 -0
- package/backend/src/flowent/workspace/events.py +180 -0
- package/backend/src/flowent/workspace/output.py +274 -0
- package/backend/src/flowent/workspace/runtime.py +1041 -0
- package/backend/uv.lock +1 -1
- package/dist/frontend/assets/index-CvWZZMtK.css +2 -0
- package/dist/frontend/assets/index-ma2v8oW7.js +90 -0
- package/dist/frontend/index.html +2 -2
- package/package.json +1 -1
- package/backend/src/flowent/static/assets/index-D7t9qNrC.js +0 -82
- package/backend/src/flowent/static/assets/index-DufpDl8x.css +0 -2
- package/dist/frontend/assets/index-D7t9qNrC.js +0 -82
- package/dist/frontend/assets/index-DufpDl8x.css +0 -2
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from fastapi import FastAPI, Query
|
|
4
|
+
from fastapi.responses import StreamingResponse
|
|
5
|
+
|
|
6
|
+
from flowent.api_models import (
|
|
7
|
+
WorkspaceClearResponse,
|
|
8
|
+
WorkspaceMessageEditRequest,
|
|
9
|
+
WorkspaceMessageEditResponse,
|
|
10
|
+
WorkspaceMessagesRequest,
|
|
11
|
+
WorkspaceRespondRequest,
|
|
12
|
+
WorkspaceRunResponse,
|
|
13
|
+
)
|
|
14
|
+
from flowent.logging import TRACE_LEVEL
|
|
15
|
+
from flowent.storage import StateStore
|
|
16
|
+
from flowent.workspace.runtime import WorkspaceRuntime
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger("flowent.routes.workspace")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def register_workspace_routes(
|
|
22
|
+
app: FastAPI,
|
|
23
|
+
*,
|
|
24
|
+
runtime: WorkspaceRuntime,
|
|
25
|
+
store: StateStore,
|
|
26
|
+
) -> None:
|
|
27
|
+
@app.put("/api/workspace/messages")
|
|
28
|
+
async def save_workspace_messages(
|
|
29
|
+
request: WorkspaceMessagesRequest,
|
|
30
|
+
) -> WorkspaceMessagesRequest:
|
|
31
|
+
return WorkspaceMessagesRequest(messages=store.save_messages(request.messages))
|
|
32
|
+
|
|
33
|
+
@app.post("/api/workspace/messages/{message_id}/edit")
|
|
34
|
+
async def edit_workspace_message(
|
|
35
|
+
message_id: str,
|
|
36
|
+
request: WorkspaceMessageEditRequest,
|
|
37
|
+
) -> WorkspaceMessageEditResponse:
|
|
38
|
+
logger.info(
|
|
39
|
+
"Workspace message edit requested action=%s message_id=%s content_length=%s",
|
|
40
|
+
request.action,
|
|
41
|
+
message_id,
|
|
42
|
+
len(request.content),
|
|
43
|
+
)
|
|
44
|
+
logger.log(TRACE_LEVEL, "Workspace edited user content=%r", request.content)
|
|
45
|
+
messages, run = runtime.edit_message(
|
|
46
|
+
message_id,
|
|
47
|
+
action=request.action,
|
|
48
|
+
content=request.content,
|
|
49
|
+
)
|
|
50
|
+
return WorkspaceMessageEditResponse(
|
|
51
|
+
messages=messages,
|
|
52
|
+
run_id=run.id if run else None,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
@app.post("/api/workspace/clear")
|
|
56
|
+
async def clear_workspace() -> WorkspaceClearResponse:
|
|
57
|
+
messages = runtime.clear()
|
|
58
|
+
await runtime.notify_cleared_runs()
|
|
59
|
+
return WorkspaceClearResponse(messages=messages)
|
|
60
|
+
|
|
61
|
+
@app.post("/api/workspace/runs")
|
|
62
|
+
async def start_workspace_run(
|
|
63
|
+
request: WorkspaceRespondRequest,
|
|
64
|
+
) -> WorkspaceRunResponse:
|
|
65
|
+
logger.info("Workspace run requested content_length=%s", len(request.content))
|
|
66
|
+
logger.log(TRACE_LEVEL, "Workspace user content=%r", request.content)
|
|
67
|
+
run = runtime.create_run(request.content, message_id=request.message_id)
|
|
68
|
+
return WorkspaceRunResponse(run_id=run.id)
|
|
69
|
+
|
|
70
|
+
@app.get("/api/workspace/runs/{run_id}/stream")
|
|
71
|
+
async def stream_workspace_run(
|
|
72
|
+
run_id: str,
|
|
73
|
+
after: int = Query(default=0, ge=0),
|
|
74
|
+
) -> StreamingResponse:
|
|
75
|
+
run = runtime.run_by_id(run_id)
|
|
76
|
+
return StreamingResponse(
|
|
77
|
+
runtime.run_stream(run, after),
|
|
78
|
+
media_type="text/event-stream",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
@app.post("/api/workspace/runs/{run_id}/stop")
|
|
82
|
+
async def stop_workspace_run(run_id: str) -> dict[str, bool]:
|
|
83
|
+
runtime.stop_run(run_id)
|
|
84
|
+
return {"ok": True}
|
|
85
|
+
|
|
86
|
+
@app.post("/api/workspace/compact", response_class=StreamingResponse)
|
|
87
|
+
async def compact_workspace() -> StreamingResponse:
|
|
88
|
+
return StreamingResponse(
|
|
89
|
+
runtime.compact_stream(),
|
|
90
|
+
media_type="text/event-stream",
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
@app.post("/api/workspace/respond")
|
|
94
|
+
async def respond_to_workspace(
|
|
95
|
+
request: WorkspaceRespondRequest,
|
|
96
|
+
) -> StreamingResponse:
|
|
97
|
+
logger.info(
|
|
98
|
+
"Workspace response requested content_length=%s", len(request.content)
|
|
99
|
+
)
|
|
100
|
+
logger.log(TRACE_LEVEL, "Workspace user content=%r", request.content)
|
|
101
|
+
run = runtime.create_run(request.content, message_id=request.message_id)
|
|
102
|
+
return StreamingResponse(
|
|
103
|
+
runtime.run_stream(run, include_snapshots=False),
|
|
104
|
+
media_type="text/event-stream",
|
|
105
|
+
)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from flowent.state.models import (
|
|
2
|
+
StoredAssistantOutputGroup,
|
|
3
|
+
StoredCompactionCheckpoint,
|
|
4
|
+
StoredErrorOutputItem,
|
|
5
|
+
StoredMcpServer,
|
|
6
|
+
StoredMcpTool,
|
|
7
|
+
StoredMessage,
|
|
8
|
+
StoredOutputItem,
|
|
9
|
+
StoredProvider,
|
|
10
|
+
StoredSettings,
|
|
11
|
+
StoredSkill,
|
|
12
|
+
StoredState,
|
|
13
|
+
StoredTelegramBot,
|
|
14
|
+
StoredTelegramSession,
|
|
15
|
+
StoredTextOutputItem,
|
|
16
|
+
StoredThinkingOutputItem,
|
|
17
|
+
StoredToolItem,
|
|
18
|
+
StoredToolOutputItem,
|
|
19
|
+
StoredWorkflow,
|
|
20
|
+
StoredWorkflowDefinition,
|
|
21
|
+
StoredWorkflowEdge,
|
|
22
|
+
StoredWorkflowNode,
|
|
23
|
+
StoredWorkflowNodePosition,
|
|
24
|
+
StoredWritablePath,
|
|
25
|
+
)
|
|
26
|
+
from flowent.state.store import StateStore
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"StateStore",
|
|
30
|
+
"StoredAssistantOutputGroup",
|
|
31
|
+
"StoredCompactionCheckpoint",
|
|
32
|
+
"StoredErrorOutputItem",
|
|
33
|
+
"StoredMcpServer",
|
|
34
|
+
"StoredMcpTool",
|
|
35
|
+
"StoredMessage",
|
|
36
|
+
"StoredOutputItem",
|
|
37
|
+
"StoredProvider",
|
|
38
|
+
"StoredSettings",
|
|
39
|
+
"StoredSkill",
|
|
40
|
+
"StoredState",
|
|
41
|
+
"StoredTelegramBot",
|
|
42
|
+
"StoredTelegramSession",
|
|
43
|
+
"StoredTextOutputItem",
|
|
44
|
+
"StoredThinkingOutputItem",
|
|
45
|
+
"StoredToolItem",
|
|
46
|
+
"StoredToolOutputItem",
|
|
47
|
+
"StoredWorkflow",
|
|
48
|
+
"StoredWorkflowDefinition",
|
|
49
|
+
"StoredWorkflowEdge",
|
|
50
|
+
"StoredWorkflowNode",
|
|
51
|
+
"StoredWorkflowNodePosition",
|
|
52
|
+
"StoredWritablePath",
|
|
53
|
+
]
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
from typing import Annotated, Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, Field, PositiveInt
|
|
4
|
+
|
|
5
|
+
from flowent.llm import ChatMessage, ProviderFormat, ReasoningEffort
|
|
6
|
+
from flowent.usage import TokenUsageInfo
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class StoredTelegramSession(BaseModel):
|
|
10
|
+
model_config = ConfigDict(extra="forbid")
|
|
11
|
+
|
|
12
|
+
chat_id: str
|
|
13
|
+
display_name: str = ""
|
|
14
|
+
recent_message: str = ""
|
|
15
|
+
status: str
|
|
16
|
+
updated_at: int = 0
|
|
17
|
+
user_id: str = ""
|
|
18
|
+
username: str = ""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class StoredTelegramBot(BaseModel):
|
|
22
|
+
model_config = ConfigDict(extra="forbid")
|
|
23
|
+
|
|
24
|
+
bot_token: str
|
|
25
|
+
enabled: bool
|
|
26
|
+
error: str = ""
|
|
27
|
+
sessions: list[StoredTelegramSession] = Field(default_factory=list)
|
|
28
|
+
status: str = "disabled"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class StoredMcpTool(BaseModel):
|
|
32
|
+
model_config = ConfigDict(extra="forbid")
|
|
33
|
+
|
|
34
|
+
description: str = ""
|
|
35
|
+
input_schema: dict[str, object] = Field(default_factory=dict)
|
|
36
|
+
name: str
|
|
37
|
+
output_schema: dict[str, object] | None = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class StoredMcpServer(BaseModel):
|
|
41
|
+
model_config = ConfigDict(extra="forbid")
|
|
42
|
+
|
|
43
|
+
args: list[str] = Field(default_factory=list)
|
|
44
|
+
command: str = ""
|
|
45
|
+
config: dict[str, object] = Field(default_factory=dict)
|
|
46
|
+
enabled: bool = True
|
|
47
|
+
error: str = ""
|
|
48
|
+
id: str
|
|
49
|
+
name: str
|
|
50
|
+
status: str = "disabled"
|
|
51
|
+
tools: list[StoredMcpTool] = Field(default_factory=list)
|
|
52
|
+
type: str
|
|
53
|
+
url: str = ""
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class StoredSkill(BaseModel):
|
|
57
|
+
model_config = ConfigDict(extra="forbid")
|
|
58
|
+
|
|
59
|
+
description: str
|
|
60
|
+
enabled: bool = True
|
|
61
|
+
error: str = ""
|
|
62
|
+
id: str
|
|
63
|
+
name: str
|
|
64
|
+
path: str
|
|
65
|
+
scope: str
|
|
66
|
+
slug: str
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class StoredWritablePath(BaseModel):
|
|
70
|
+
model_config = ConfigDict(extra="forbid")
|
|
71
|
+
|
|
72
|
+
created_at: int = 0
|
|
73
|
+
path: str
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class StoredWorkflowNodePosition(BaseModel):
|
|
77
|
+
model_config = ConfigDict(extra="forbid")
|
|
78
|
+
|
|
79
|
+
x: float = 0
|
|
80
|
+
y: float = 0
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class StoredWorkflowNode(BaseModel):
|
|
84
|
+
model_config = ConfigDict(extra="forbid")
|
|
85
|
+
|
|
86
|
+
data: dict[str, object] = Field(default_factory=dict)
|
|
87
|
+
description: str = ""
|
|
88
|
+
id: str
|
|
89
|
+
name: str
|
|
90
|
+
position: StoredWorkflowNodePosition = Field(
|
|
91
|
+
default_factory=StoredWorkflowNodePosition
|
|
92
|
+
)
|
|
93
|
+
type: Literal["input", "agent", "merge", "output"]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class StoredWorkflowEdge(BaseModel):
|
|
97
|
+
model_config = ConfigDict(extra="forbid")
|
|
98
|
+
|
|
99
|
+
id: str
|
|
100
|
+
label: str = ""
|
|
101
|
+
source: str
|
|
102
|
+
source_handle: str = ""
|
|
103
|
+
target: str
|
|
104
|
+
target_handle: str = ""
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class StoredWorkflowDefinition(BaseModel):
|
|
108
|
+
model_config = ConfigDict(extra="forbid")
|
|
109
|
+
|
|
110
|
+
edges: list[StoredWorkflowEdge] = Field(default_factory=list)
|
|
111
|
+
nodes: list[StoredWorkflowNode] = Field(default_factory=list)
|
|
112
|
+
version: int = 1
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class StoredWorkflow(BaseModel):
|
|
116
|
+
model_config = ConfigDict(extra="forbid")
|
|
117
|
+
|
|
118
|
+
created_at: int = 0
|
|
119
|
+
definition: StoredWorkflowDefinition
|
|
120
|
+
id: str
|
|
121
|
+
name: str
|
|
122
|
+
updated_at: int = 0
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class StoredProvider(BaseModel):
|
|
126
|
+
model_config = ConfigDict(extra="forbid")
|
|
127
|
+
|
|
128
|
+
api_key: str
|
|
129
|
+
base_url: str
|
|
130
|
+
id: str
|
|
131
|
+
models: list[str]
|
|
132
|
+
name: str
|
|
133
|
+
type: ProviderFormat
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class StoredSettings(BaseModel):
|
|
137
|
+
model_config = ConfigDict(extra="forbid")
|
|
138
|
+
|
|
139
|
+
agent_prompt: str = Field(default="", exclude_if=lambda value: value == "")
|
|
140
|
+
context_window_limit: PositiveInt | None = None
|
|
141
|
+
reasoning_effort: ReasoningEffort = ReasoningEffort.DEFAULT
|
|
142
|
+
selected_model: str
|
|
143
|
+
selected_provider_id: str
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class StoredToolItem(BaseModel):
|
|
147
|
+
model_config = ConfigDict(extra="forbid")
|
|
148
|
+
|
|
149
|
+
id: str
|
|
150
|
+
name: str
|
|
151
|
+
status: str
|
|
152
|
+
title: str
|
|
153
|
+
arguments: dict[str, object] | None = None
|
|
154
|
+
content: str | None = None
|
|
155
|
+
data: dict[str, object] | None = None
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class StoredThinkingOutputItem(BaseModel):
|
|
159
|
+
model_config = ConfigDict(extra="forbid")
|
|
160
|
+
|
|
161
|
+
content: str
|
|
162
|
+
id: str
|
|
163
|
+
type: Literal["thinking"]
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class StoredTextOutputItem(BaseModel):
|
|
167
|
+
model_config = ConfigDict(extra="forbid")
|
|
168
|
+
|
|
169
|
+
content: str
|
|
170
|
+
id: str
|
|
171
|
+
type: Literal["text"]
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class StoredErrorOutputItem(BaseModel):
|
|
175
|
+
model_config = ConfigDict(extra="forbid")
|
|
176
|
+
|
|
177
|
+
detail: str = Field(default="", exclude_if=lambda value: value == "")
|
|
178
|
+
id: str
|
|
179
|
+
message: str
|
|
180
|
+
title: str
|
|
181
|
+
type: Literal["error"]
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class StoredToolOutputItem(BaseModel):
|
|
185
|
+
model_config = ConfigDict(extra="forbid")
|
|
186
|
+
|
|
187
|
+
id: str
|
|
188
|
+
tool: StoredToolItem
|
|
189
|
+
type: Literal["tool"]
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
StoredOutputItem = Annotated[
|
|
193
|
+
StoredThinkingOutputItem
|
|
194
|
+
| StoredTextOutputItem
|
|
195
|
+
| StoredErrorOutputItem
|
|
196
|
+
| StoredToolOutputItem,
|
|
197
|
+
Field(discriminator="type"),
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class StoredAssistantOutputGroup(BaseModel):
|
|
202
|
+
model_config = ConfigDict(extra="forbid")
|
|
203
|
+
|
|
204
|
+
id: str
|
|
205
|
+
items: list[StoredOutputItem]
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class StoredMessage(BaseModel):
|
|
209
|
+
model_config = ConfigDict(extra="forbid")
|
|
210
|
+
|
|
211
|
+
author: str
|
|
212
|
+
content: str
|
|
213
|
+
groups: list[StoredAssistantOutputGroup] = Field(
|
|
214
|
+
default_factory=list, exclude_if=lambda value: value == []
|
|
215
|
+
)
|
|
216
|
+
id: str
|
|
217
|
+
status: str = Field(
|
|
218
|
+
default="completed", exclude_if=lambda value: value == "completed"
|
|
219
|
+
)
|
|
220
|
+
thinking: str = Field(default="", exclude_if=lambda value: value == "")
|
|
221
|
+
tools: list[StoredToolItem] = Field(default_factory=list)
|
|
222
|
+
usage_info: TokenUsageInfo | None = Field(
|
|
223
|
+
default=None, exclude_if=lambda value: value is None
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class StoredCompactionCheckpoint(BaseModel):
|
|
228
|
+
model_config = ConfigDict(extra="forbid")
|
|
229
|
+
|
|
230
|
+
created_at: int = 0
|
|
231
|
+
id: str
|
|
232
|
+
method: str
|
|
233
|
+
replacement_history: list[ChatMessage]
|
|
234
|
+
source_message_id: str | None = None
|
|
235
|
+
summary: str
|
|
236
|
+
token_after: int = 0
|
|
237
|
+
token_before: int = 0
|
|
238
|
+
trigger: str
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class StoredState(BaseModel):
|
|
242
|
+
model_config = ConfigDict(extra="forbid")
|
|
243
|
+
|
|
244
|
+
active_run_event_index: int = 0
|
|
245
|
+
active_run_id: str | None = None
|
|
246
|
+
is_compacting: bool = False
|
|
247
|
+
mcp_servers: list[StoredMcpServer]
|
|
248
|
+
messages: list[StoredMessage]
|
|
249
|
+
providers: list[StoredProvider]
|
|
250
|
+
settings: StoredSettings
|
|
251
|
+
skills: list[StoredSkill]
|
|
252
|
+
telegram_bot: StoredTelegramBot
|
|
253
|
+
usage_info: TokenUsageInfo | None = Field(
|
|
254
|
+
default=None, exclude_if=lambda value: value is None
|
|
255
|
+
)
|
|
256
|
+
writable_paths: list[StoredWritablePath] = Field(default_factory=list)
|
|
257
|
+
workflows: list[StoredWorkflow] = Field(default_factory=list)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def migrate(connection: sqlite3.Connection) -> None:
|
|
5
|
+
connection.executescript(
|
|
6
|
+
"""
|
|
7
|
+
CREATE TABLE IF NOT EXISTS workflows (
|
|
8
|
+
id TEXT PRIMARY KEY,
|
|
9
|
+
name TEXT NOT NULL,
|
|
10
|
+
definition TEXT NOT NULL,
|
|
11
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
12
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
CREATE TABLE IF NOT EXISTS mcp_servers (
|
|
16
|
+
id TEXT PRIMARY KEY,
|
|
17
|
+
name TEXT NOT NULL,
|
|
18
|
+
type TEXT NOT NULL,
|
|
19
|
+
command TEXT NOT NULL DEFAULT '',
|
|
20
|
+
args TEXT NOT NULL DEFAULT '[]',
|
|
21
|
+
config TEXT NOT NULL DEFAULT '{}',
|
|
22
|
+
url TEXT NOT NULL DEFAULT '',
|
|
23
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
24
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
25
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
CREATE TABLE IF NOT EXISTS mcp_tools (
|
|
29
|
+
server_id TEXT NOT NULL REFERENCES mcp_servers(id) ON DELETE CASCADE,
|
|
30
|
+
name TEXT NOT NULL,
|
|
31
|
+
description TEXT NOT NULL DEFAULT '',
|
|
32
|
+
input_schema TEXT NOT NULL DEFAULT '{}',
|
|
33
|
+
output_schema TEXT,
|
|
34
|
+
position INTEGER NOT NULL,
|
|
35
|
+
PRIMARY KEY (server_id, name)
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
CREATE TABLE IF NOT EXISTS telegram_bot (
|
|
39
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
40
|
+
enabled INTEGER NOT NULL DEFAULT 0,
|
|
41
|
+
bot_token TEXT NOT NULL,
|
|
42
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
CREATE TABLE IF NOT EXISTS telegram_sessions (
|
|
46
|
+
chat_id TEXT PRIMARY KEY,
|
|
47
|
+
user_id TEXT NOT NULL DEFAULT '',
|
|
48
|
+
username TEXT NOT NULL DEFAULT '',
|
|
49
|
+
display_name TEXT NOT NULL DEFAULT '',
|
|
50
|
+
recent_message TEXT NOT NULL DEFAULT '',
|
|
51
|
+
status TEXT NOT NULL,
|
|
52
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
CREATE TABLE IF NOT EXISTS providers (
|
|
56
|
+
id TEXT PRIMARY KEY,
|
|
57
|
+
name TEXT NOT NULL,
|
|
58
|
+
type TEXT NOT NULL,
|
|
59
|
+
base_url TEXT NOT NULL,
|
|
60
|
+
api_key TEXT NOT NULL,
|
|
61
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch()),
|
|
62
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
CREATE TABLE IF NOT EXISTS provider_models (
|
|
66
|
+
provider_id TEXT NOT NULL REFERENCES providers(id) ON DELETE CASCADE,
|
|
67
|
+
model TEXT NOT NULL,
|
|
68
|
+
position INTEGER NOT NULL,
|
|
69
|
+
PRIMARY KEY (provider_id, model)
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
CREATE TABLE IF NOT EXISTS settings (
|
|
73
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
74
|
+
selected_provider_id TEXT NOT NULL DEFAULT '',
|
|
75
|
+
selected_model TEXT NOT NULL DEFAULT '',
|
|
76
|
+
reasoning_effort TEXT NOT NULL DEFAULT 'default',
|
|
77
|
+
agent_prompt TEXT NOT NULL DEFAULT '',
|
|
78
|
+
context_window_limit INTEGER,
|
|
79
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
CREATE TABLE IF NOT EXISTS messages (
|
|
83
|
+
id TEXT PRIMARY KEY,
|
|
84
|
+
author TEXT NOT NULL,
|
|
85
|
+
content TEXT NOT NULL,
|
|
86
|
+
status TEXT NOT NULL DEFAULT 'completed',
|
|
87
|
+
usage_info TEXT,
|
|
88
|
+
position INTEGER NOT NULL
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
CREATE TABLE IF NOT EXISTS workspace_context (
|
|
92
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
93
|
+
compacted_summary TEXT NOT NULL DEFAULT '',
|
|
94
|
+
active_compaction_id TEXT,
|
|
95
|
+
is_compacting INTEGER NOT NULL DEFAULT 0,
|
|
96
|
+
usage_info TEXT,
|
|
97
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
CREATE TABLE IF NOT EXISTS compaction_checkpoints (
|
|
101
|
+
id TEXT PRIMARY KEY,
|
|
102
|
+
trigger TEXT NOT NULL,
|
|
103
|
+
method TEXT NOT NULL,
|
|
104
|
+
summary TEXT NOT NULL,
|
|
105
|
+
replacement_history TEXT NOT NULL DEFAULT '[]',
|
|
106
|
+
source_message_id TEXT,
|
|
107
|
+
token_before INTEGER NOT NULL DEFAULT 0,
|
|
108
|
+
token_after INTEGER NOT NULL DEFAULT 0,
|
|
109
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
CREATE TABLE IF NOT EXISTS skill_settings (
|
|
113
|
+
id TEXT PRIMARY KEY,
|
|
114
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
115
|
+
updated_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
CREATE TABLE IF NOT EXISTS writable_paths (
|
|
119
|
+
path TEXT PRIMARY KEY,
|
|
120
|
+
created_at INTEGER NOT NULL DEFAULT (unixepoch())
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
124
|
+
version INTEGER PRIMARY KEY
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
INSERT OR IGNORE INTO schema_migrations (version) VALUES (1);
|
|
128
|
+
"""
|
|
129
|
+
)
|
|
130
|
+
mcp_server_columns = table_columns(connection, "mcp_servers")
|
|
131
|
+
if "config" not in mcp_server_columns:
|
|
132
|
+
connection.execute(
|
|
133
|
+
"""
|
|
134
|
+
ALTER TABLE mcp_servers
|
|
135
|
+
ADD COLUMN config TEXT NOT NULL DEFAULT '{}'
|
|
136
|
+
"""
|
|
137
|
+
)
|
|
138
|
+
message_columns = table_columns(connection, "messages")
|
|
139
|
+
if "tools" not in message_columns:
|
|
140
|
+
connection.execute(
|
|
141
|
+
"ALTER TABLE messages ADD COLUMN tools TEXT NOT NULL DEFAULT '[]'"
|
|
142
|
+
)
|
|
143
|
+
if "thinking" not in message_columns:
|
|
144
|
+
connection.execute(
|
|
145
|
+
"ALTER TABLE messages ADD COLUMN thinking TEXT NOT NULL DEFAULT ''"
|
|
146
|
+
)
|
|
147
|
+
if "status" not in message_columns:
|
|
148
|
+
connection.execute(
|
|
149
|
+
"ALTER TABLE messages ADD COLUMN status TEXT NOT NULL DEFAULT 'completed'"
|
|
150
|
+
)
|
|
151
|
+
if "groups" not in message_columns:
|
|
152
|
+
connection.execute(
|
|
153
|
+
"ALTER TABLE messages ADD COLUMN groups TEXT NOT NULL DEFAULT '[]'"
|
|
154
|
+
)
|
|
155
|
+
if "usage_info" not in message_columns:
|
|
156
|
+
connection.execute("ALTER TABLE messages ADD COLUMN usage_info TEXT")
|
|
157
|
+
settings_columns = table_columns(connection, "settings")
|
|
158
|
+
if "reasoning_effort" not in settings_columns:
|
|
159
|
+
connection.execute(
|
|
160
|
+
"ALTER TABLE settings "
|
|
161
|
+
"ADD COLUMN reasoning_effort TEXT NOT NULL DEFAULT 'default'"
|
|
162
|
+
)
|
|
163
|
+
if "agent_prompt" not in settings_columns:
|
|
164
|
+
connection.execute(
|
|
165
|
+
"ALTER TABLE settings ADD COLUMN agent_prompt TEXT NOT NULL DEFAULT ''"
|
|
166
|
+
)
|
|
167
|
+
if "context_window_limit" not in settings_columns:
|
|
168
|
+
connection.execute(
|
|
169
|
+
"ALTER TABLE settings ADD COLUMN context_window_limit INTEGER"
|
|
170
|
+
)
|
|
171
|
+
workspace_context_columns = table_columns(connection, "workspace_context")
|
|
172
|
+
if "active_compaction_id" not in workspace_context_columns:
|
|
173
|
+
connection.execute(
|
|
174
|
+
"ALTER TABLE workspace_context ADD COLUMN active_compaction_id TEXT"
|
|
175
|
+
)
|
|
176
|
+
if "usage_info" not in workspace_context_columns:
|
|
177
|
+
connection.execute("ALTER TABLE workspace_context ADD COLUMN usage_info TEXT")
|
|
178
|
+
if "is_compacting" not in workspace_context_columns:
|
|
179
|
+
connection.execute(
|
|
180
|
+
"ALTER TABLE workspace_context "
|
|
181
|
+
"ADD COLUMN is_compacting INTEGER NOT NULL DEFAULT 0"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def table_columns(connection: sqlite3.Connection, table: str) -> set[str]:
|
|
186
|
+
return {row["name"] for row in connection.execute(f"PRAGMA table_info({table})")}
|