PraisonAI 1.0.5__tar.gz → 1.0.8__tar.gz
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.
Potentially problematic release.
This version of PraisonAI might be problematic. Click here for more details.
- {praisonai-1.0.5 → praisonai-1.0.8}/PKG-INFO +16 -3
- {praisonai-1.0.5 → praisonai-1.0.8}/README.md +15 -2
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/deploy.py +1 -1
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/chat.py +60 -257
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/code.py +30 -130
- praisonai-1.0.8/praisonai/ui/db.py +242 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/sql_alchemy.py +5 -3
- {praisonai-1.0.5 → praisonai-1.0.8}/pyproject.toml +11 -1
- {praisonai-1.0.5 → praisonai-1.0.8}/LICENSE +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/__init__.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/__main__.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/agents_generator.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/api/call.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/auto.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/chainlit_ui.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/cli.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/inbuilt_tools/__init__.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/inbuilt_tools/autogen_tools.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/inc/__init__.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/inc/config.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/inc/models.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/public/android-chrome-192x192.png +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/public/android-chrome-512x512.png +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/public/apple-touch-icon.png +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/public/fantasy.svg +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/public/favicon-16x16.png +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/public/favicon-32x32.png +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/public/favicon.ico +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/public/game.svg +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/public/logo_dark.png +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/public/logo_light.png +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/public/movie.svg +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/public/thriller.svg +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/setup/__init__.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/setup/build.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/setup/config.yaml +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/setup/post_install.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/setup/setup_conda_env.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/setup/setup_conda_env.sh +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/setup.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/test.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/train.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/README.md +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/context.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/public/fantasy.svg +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/public/game.svg +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/public/logo_dark.png +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/public/logo_light.png +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/public/movie.svg +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/public/thriller.svg +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/realtime.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/realtimeclient/__init__.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/realtimeclient/realtimedocs.txt +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/ui/realtimeclient/tools.py +0 -0
- {praisonai-1.0.5 → praisonai-1.0.8}/praisonai/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PraisonAI
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8
|
|
4
4
|
Summary: PraisonAI application combines AutoGen and CrewAI or similar frameworks into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customization, and efficient human-agent collaboration.
|
|
5
5
|
Author: Mervin Praison
|
|
6
6
|
Requires-Python: >=3.10,<3.13
|
|
@@ -116,11 +116,24 @@ Praison AI, leveraging both AutoGen and CrewAI or any other agent framework, rep
|
|
|
116
116
|
|
|
117
117
|
## Installation Options
|
|
118
118
|
|
|
119
|
-
###
|
|
119
|
+
### Using pip
|
|
120
120
|
```bash
|
|
121
121
|
pip install praisonai
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
+
### Using uv (Fast Python Package Installer)
|
|
125
|
+
```bash
|
|
126
|
+
# Install uv if you haven't already
|
|
127
|
+
pip install uv
|
|
128
|
+
|
|
129
|
+
# Install from requirements
|
|
130
|
+
uv pip install -r pyproject.toml
|
|
131
|
+
|
|
132
|
+
# Install with extras
|
|
133
|
+
uv pip install -r pyproject.toml --extra code
|
|
134
|
+
uv pip install -r pyproject.toml --extra "crewai,autogen"
|
|
135
|
+
```
|
|
136
|
+
|
|
124
137
|
### Framework-specific Installation
|
|
125
138
|
```bash
|
|
126
139
|
# Install with CrewAI support
|
|
@@ -435,6 +448,7 @@ if __name__ == "__main__":
|
|
|
435
448
|
```
|
|
436
449
|
|
|
437
450
|
4. **Install only dev dependencies:**
|
|
451
|
+
|
|
438
452
|
```sh
|
|
439
453
|
poetry install --with dev
|
|
440
454
|
```
|
|
@@ -480,4 +494,3 @@ Praison AI is an open-sourced software licensed under the **[MIT license](https:
|
|
|
480
494
|
|
|
481
495
|
Praison AI is an open-sourced software licensed under the **[MIT license](https://opensource.org/licenses/MIT)**.
|
|
482
496
|
|
|
483
|
-
|
|
@@ -53,11 +53,24 @@ Praison AI, leveraging both AutoGen and CrewAI or any other agent framework, rep
|
|
|
53
53
|
|
|
54
54
|
## Installation Options
|
|
55
55
|
|
|
56
|
-
###
|
|
56
|
+
### Using pip
|
|
57
57
|
```bash
|
|
58
58
|
pip install praisonai
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
### Using uv (Fast Python Package Installer)
|
|
62
|
+
```bash
|
|
63
|
+
# Install uv if you haven't already
|
|
64
|
+
pip install uv
|
|
65
|
+
|
|
66
|
+
# Install from requirements
|
|
67
|
+
uv pip install -r pyproject.toml
|
|
68
|
+
|
|
69
|
+
# Install with extras
|
|
70
|
+
uv pip install -r pyproject.toml --extra code
|
|
71
|
+
uv pip install -r pyproject.toml --extra "crewai,autogen"
|
|
72
|
+
```
|
|
73
|
+
|
|
61
74
|
### Framework-specific Installation
|
|
62
75
|
```bash
|
|
63
76
|
# Install with CrewAI support
|
|
@@ -372,6 +385,7 @@ if __name__ == "__main__":
|
|
|
372
385
|
```
|
|
373
386
|
|
|
374
387
|
4. **Install only dev dependencies:**
|
|
388
|
+
|
|
375
389
|
```sh
|
|
376
390
|
poetry install --with dev
|
|
377
391
|
```
|
|
@@ -416,4 +430,3 @@ Praison AI is an open-sourced software licensed under the **[MIT license](https:
|
|
|
416
430
|
## License
|
|
417
431
|
|
|
418
432
|
Praison AI is an open-sourced software licensed under the **[MIT license](https://opensource.org/licenses/MIT)**.
|
|
419
|
-
|
|
@@ -56,7 +56,7 @@ class CloudDeployer:
|
|
|
56
56
|
file.write("FROM python:3.11-slim\n")
|
|
57
57
|
file.write("WORKDIR /app\n")
|
|
58
58
|
file.write("COPY . .\n")
|
|
59
|
-
file.write("RUN pip install flask praisonai==1.0.
|
|
59
|
+
file.write("RUN pip install flask praisonai==1.0.8 gunicorn markdown\n")
|
|
60
60
|
file.write("EXPOSE 8080\n")
|
|
61
61
|
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
|
62
62
|
|
|
@@ -1,29 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
from chainlit.input_widget import TextInput
|
|
3
|
-
from chainlit.types import ThreadDict
|
|
4
|
-
from litellm import acompletion
|
|
1
|
+
# Standard library imports
|
|
5
2
|
import os
|
|
6
|
-
import sqlite3
|
|
7
3
|
from datetime import datetime
|
|
8
|
-
from typing import Dict,
|
|
9
|
-
from dotenv import load_dotenv
|
|
10
|
-
load_dotenv()
|
|
11
|
-
import chainlit.data as cl_data
|
|
12
|
-
from chainlit.step import StepDict
|
|
13
|
-
from literalai.helper import utc_now
|
|
4
|
+
from typing import Dict, Optional
|
|
14
5
|
import logging
|
|
15
6
|
import json
|
|
16
|
-
from sql_alchemy import SQLAlchemyDataLayer
|
|
17
|
-
from tavily import TavilyClient
|
|
18
|
-
from crawl4ai import AsyncWebCrawler
|
|
19
7
|
import asyncio
|
|
20
|
-
from PIL import Image
|
|
21
8
|
import io
|
|
22
9
|
import base64
|
|
23
10
|
|
|
24
|
-
|
|
25
|
-
from
|
|
11
|
+
# Third-party imports
|
|
12
|
+
from dotenv import load_dotenv
|
|
13
|
+
from PIL import Image
|
|
14
|
+
from tavily import TavilyClient
|
|
15
|
+
from crawl4ai import AsyncWebCrawler
|
|
26
16
|
|
|
17
|
+
# Local application/library imports
|
|
18
|
+
import chainlit as cl
|
|
19
|
+
from chainlit.input_widget import TextInput
|
|
20
|
+
from chainlit.types import ThreadDict
|
|
21
|
+
import chainlit.data as cl_data
|
|
22
|
+
from litellm import acompletion
|
|
23
|
+
from literalai.helper import utc_now
|
|
24
|
+
from db import DatabaseManager
|
|
25
|
+
|
|
26
|
+
# Load environment variables
|
|
27
|
+
load_dotenv()
|
|
28
|
+
|
|
29
|
+
# Logging configuration
|
|
27
30
|
logger = logging.getLogger(__name__)
|
|
28
31
|
log_level = os.getenv("LOGLEVEL", "INFO").upper()
|
|
29
32
|
logger.handlers = []
|
|
@@ -42,233 +45,19 @@ if not CHAINLIT_AUTH_SECRET:
|
|
|
42
45
|
now = utc_now()
|
|
43
46
|
create_step_counter = 0
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if not DATABASE_URL:
|
|
49
|
-
return
|
|
50
|
-
engine = create_async_engine(DATABASE_URL, echo=False)
|
|
51
|
-
async with engine.begin() as conn:
|
|
52
|
-
# Use double quotes for column names and renamed start/end to startTime/endTime
|
|
53
|
-
await conn.execute(text('''
|
|
54
|
-
CREATE TABLE IF NOT EXISTS users (
|
|
55
|
-
"id" TEXT PRIMARY KEY,
|
|
56
|
-
"identifier" TEXT NOT NULL UNIQUE,
|
|
57
|
-
"meta" TEXT NOT NULL DEFAULT '{}',
|
|
58
|
-
"createdAt" TEXT
|
|
59
|
-
);
|
|
60
|
-
'''))
|
|
61
|
-
await conn.execute(text('''
|
|
62
|
-
CREATE TABLE IF NOT EXISTS threads (
|
|
63
|
-
"id" TEXT PRIMARY KEY,
|
|
64
|
-
"createdAt" TEXT,
|
|
65
|
-
"name" TEXT,
|
|
66
|
-
"userId" TEXT,
|
|
67
|
-
"userIdentifier" TEXT,
|
|
68
|
-
"tags" TEXT DEFAULT '[]',
|
|
69
|
-
"meta" TEXT NOT NULL DEFAULT '{}',
|
|
70
|
-
FOREIGN KEY ("userId") REFERENCES users("id") ON DELETE CASCADE
|
|
71
|
-
);
|
|
72
|
-
'''))
|
|
73
|
-
await conn.execute(text('''
|
|
74
|
-
CREATE TABLE IF NOT EXISTS steps (
|
|
75
|
-
"id" TEXT PRIMARY KEY,
|
|
76
|
-
"name" TEXT NOT NULL,
|
|
77
|
-
"type" TEXT NOT NULL,
|
|
78
|
-
"threadId" TEXT NOT NULL,
|
|
79
|
-
"parentId" TEXT,
|
|
80
|
-
"disableFeedback" BOOLEAN NOT NULL DEFAULT FALSE,
|
|
81
|
-
"streaming" BOOLEAN NOT NULL DEFAULT FALSE,
|
|
82
|
-
"waitForAnswer" BOOLEAN DEFAULT FALSE,
|
|
83
|
-
"isError" BOOLEAN NOT NULL DEFAULT FALSE,
|
|
84
|
-
"meta" TEXT DEFAULT '{}',
|
|
85
|
-
"tags" TEXT DEFAULT '[]',
|
|
86
|
-
"input" TEXT,
|
|
87
|
-
"output" TEXT,
|
|
88
|
-
"createdAt" TEXT,
|
|
89
|
-
"startTime" TEXT,
|
|
90
|
-
"endTime" TEXT,
|
|
91
|
-
"generation" TEXT,
|
|
92
|
-
"showInput" TEXT,
|
|
93
|
-
"language" TEXT,
|
|
94
|
-
"indent" INT,
|
|
95
|
-
FOREIGN KEY ("threadId") REFERENCES threads("id") ON DELETE CASCADE
|
|
96
|
-
);
|
|
97
|
-
'''))
|
|
98
|
-
await conn.execute(text('''
|
|
99
|
-
CREATE TABLE IF NOT EXISTS elements (
|
|
100
|
-
"id" TEXT PRIMARY KEY,
|
|
101
|
-
"threadId" TEXT,
|
|
102
|
-
"type" TEXT,
|
|
103
|
-
"url" TEXT,
|
|
104
|
-
"chainlitKey" TEXT,
|
|
105
|
-
"name" TEXT NOT NULL,
|
|
106
|
-
"display" TEXT,
|
|
107
|
-
"objectKey" TEXT,
|
|
108
|
-
"size" TEXT,
|
|
109
|
-
"page" INT,
|
|
110
|
-
"language" TEXT,
|
|
111
|
-
"forId" TEXT,
|
|
112
|
-
"mime" TEXT,
|
|
113
|
-
FOREIGN KEY ("threadId") REFERENCES threads("id") ON DELETE CASCADE
|
|
114
|
-
);
|
|
115
|
-
'''))
|
|
116
|
-
await conn.execute(text('''
|
|
117
|
-
CREATE TABLE IF NOT EXISTS feedbacks (
|
|
118
|
-
"id" TEXT PRIMARY KEY,
|
|
119
|
-
"forId" TEXT NOT NULL,
|
|
120
|
-
"value" INT NOT NULL,
|
|
121
|
-
"threadId" TEXT,
|
|
122
|
-
"comment" TEXT
|
|
123
|
-
);
|
|
124
|
-
'''))
|
|
125
|
-
await conn.execute(text('''
|
|
126
|
-
CREATE TABLE IF NOT EXISTS settings (
|
|
127
|
-
"id" SERIAL PRIMARY KEY,
|
|
128
|
-
"key" TEXT UNIQUE,
|
|
129
|
-
"value" TEXT
|
|
130
|
-
);
|
|
131
|
-
'''))
|
|
132
|
-
await engine.dispose()
|
|
133
|
-
|
|
134
|
-
def create_schema_sqlite(db_path: str):
|
|
135
|
-
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
|
136
|
-
conn = sqlite3.connect(db_path)
|
|
137
|
-
cursor = conn.cursor()
|
|
138
|
-
# Renamed start to startTime, end to endTime
|
|
139
|
-
cursor.execute('''
|
|
140
|
-
CREATE TABLE IF NOT EXISTS users (
|
|
141
|
-
"id" TEXT PRIMARY KEY,
|
|
142
|
-
"identifier" TEXT NOT NULL UNIQUE,
|
|
143
|
-
"meta" TEXT NOT NULL DEFAULT '{}',
|
|
144
|
-
"createdAt" TEXT
|
|
145
|
-
)
|
|
146
|
-
''')
|
|
147
|
-
cursor.execute('''
|
|
148
|
-
CREATE TABLE IF NOT EXISTS threads (
|
|
149
|
-
"id" TEXT PRIMARY KEY,
|
|
150
|
-
"createdAt" TEXT,
|
|
151
|
-
"name" TEXT,
|
|
152
|
-
"userId" TEXT,
|
|
153
|
-
"userIdentifier" TEXT,
|
|
154
|
-
"tags" TEXT DEFAULT '[]',
|
|
155
|
-
"meta" TEXT NOT NULL DEFAULT '{}',
|
|
156
|
-
FOREIGN KEY ("userId") REFERENCES users("id") ON DELETE CASCADE
|
|
157
|
-
)
|
|
158
|
-
''')
|
|
159
|
-
cursor.execute('''
|
|
160
|
-
CREATE TABLE IF NOT EXISTS steps (
|
|
161
|
-
"id" TEXT PRIMARY KEY,
|
|
162
|
-
"name" TEXT NOT NULL,
|
|
163
|
-
"type" TEXT NOT NULL,
|
|
164
|
-
"threadId" TEXT NOT NULL,
|
|
165
|
-
"parentId" TEXT,
|
|
166
|
-
"disableFeedback" BOOLEAN NOT NULL DEFAULT 0,
|
|
167
|
-
"streaming" BOOLEAN NOT NULL DEFAULT 0,
|
|
168
|
-
"waitForAnswer" BOOLEAN DEFAULT 0,
|
|
169
|
-
"isError" BOOLEAN NOT NULL DEFAULT 0,
|
|
170
|
-
"meta" TEXT DEFAULT '{}',
|
|
171
|
-
"tags" TEXT DEFAULT '[]',
|
|
172
|
-
"input" TEXT,
|
|
173
|
-
"output" TEXT,
|
|
174
|
-
"createdAt" TEXT,
|
|
175
|
-
"startTime" TEXT,
|
|
176
|
-
"endTime" TEXT,
|
|
177
|
-
"generation" TEXT,
|
|
178
|
-
"showInput" TEXT,
|
|
179
|
-
"language" TEXT,
|
|
180
|
-
"indent" INT,
|
|
181
|
-
FOREIGN KEY ("threadId") REFERENCES threads ("id") ON DELETE CASCADE
|
|
182
|
-
)
|
|
183
|
-
''')
|
|
184
|
-
cursor.execute('''
|
|
185
|
-
CREATE TABLE IF NOT EXISTS elements (
|
|
186
|
-
"id" TEXT PRIMARY KEY,
|
|
187
|
-
"threadId" TEXT,
|
|
188
|
-
"type" TEXT,
|
|
189
|
-
"url" TEXT,
|
|
190
|
-
"chainlitKey" TEXT,
|
|
191
|
-
"name" TEXT NOT NULL,
|
|
192
|
-
"display" TEXT,
|
|
193
|
-
"objectKey" TEXT,
|
|
194
|
-
"size" TEXT,
|
|
195
|
-
"page" INT,
|
|
196
|
-
"language" TEXT,
|
|
197
|
-
"forId" TEXT,
|
|
198
|
-
"mime" TEXT,
|
|
199
|
-
FOREIGN KEY ("threadId") REFERENCES threads ("id") ON DELETE CASCADE
|
|
200
|
-
)
|
|
201
|
-
''')
|
|
202
|
-
cursor.execute('''
|
|
203
|
-
CREATE TABLE IF NOT EXISTS feedbacks (
|
|
204
|
-
"id" TEXT PRIMARY KEY,
|
|
205
|
-
"forId" TEXT NOT NULL,
|
|
206
|
-
"value" INT NOT NULL,
|
|
207
|
-
"threadId" TEXT,
|
|
208
|
-
"comment" TEXT
|
|
209
|
-
)
|
|
210
|
-
''')
|
|
211
|
-
cursor.execute('''
|
|
212
|
-
CREATE TABLE IF NOT EXISTS settings (
|
|
213
|
-
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
214
|
-
"key" TEXT UNIQUE,
|
|
215
|
-
"value" TEXT
|
|
216
|
-
)
|
|
217
|
-
''')
|
|
218
|
-
conn.commit()
|
|
219
|
-
conn.close()
|
|
220
|
-
|
|
221
|
-
if DATABASE_URL:
|
|
222
|
-
asyncio.run(create_schema_async())
|
|
223
|
-
conninfo = DATABASE_URL
|
|
224
|
-
else:
|
|
225
|
-
DB_PATH = os.path.expanduser("~/.praison/database.sqlite")
|
|
226
|
-
create_schema_sqlite(DB_PATH)
|
|
227
|
-
conninfo = f"sqlite+aiosqlite:///{DB_PATH}"
|
|
48
|
+
# Initialize database
|
|
49
|
+
db_manager = DatabaseManager()
|
|
50
|
+
db_manager.initialize()
|
|
228
51
|
|
|
229
52
|
def save_setting(key: str, value: str):
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
engine = create_async_engine(DATABASE_URL, echo=False)
|
|
233
|
-
async with engine.begin() as conn:
|
|
234
|
-
await conn.execute(text("""
|
|
235
|
-
INSERT INTO settings ("key", "value") VALUES (:key, :value)
|
|
236
|
-
ON CONFLICT ("key") DO UPDATE SET "value" = EXCLUDED."value"
|
|
237
|
-
"""), {"key": key, "value": value})
|
|
238
|
-
await engine.dispose()
|
|
239
|
-
asyncio.run(save_setting_async())
|
|
240
|
-
else:
|
|
241
|
-
conn = sqlite3.connect(DB_PATH)
|
|
242
|
-
cursor = conn.cursor()
|
|
243
|
-
cursor.execute(
|
|
244
|
-
"""
|
|
245
|
-
INSERT OR REPLACE INTO settings (id, "key", "value")
|
|
246
|
-
VALUES ((SELECT id FROM settings WHERE "key" = ?), ?, ?)
|
|
247
|
-
""",
|
|
248
|
-
(key, key, value),
|
|
249
|
-
)
|
|
250
|
-
conn.commit()
|
|
251
|
-
conn.close()
|
|
53
|
+
"""Save a setting to the database"""
|
|
54
|
+
asyncio.run(db_manager.save_setting(key, value))
|
|
252
55
|
|
|
253
56
|
def load_setting(key: str) -> str:
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
engine = create_async_engine(DATABASE_URL, echo=False)
|
|
257
|
-
async with engine.connect() as conn:
|
|
258
|
-
result = await conn.execute(text('SELECT "value" FROM settings WHERE "key" = :key'), {"key": key})
|
|
259
|
-
row = result.fetchone()
|
|
260
|
-
await engine.dispose()
|
|
261
|
-
return row[0] if row else None
|
|
262
|
-
return asyncio.run(load_setting_async())
|
|
263
|
-
else:
|
|
264
|
-
conn = sqlite3.connect(DB_PATH)
|
|
265
|
-
cursor = conn.cursor()
|
|
266
|
-
cursor.execute('SELECT "value" FROM settings WHERE "key" = ?', (key,))
|
|
267
|
-
result = cursor.fetchone()
|
|
268
|
-
conn.close()
|
|
269
|
-
return result[0] if result else None
|
|
57
|
+
"""Load a setting from the database"""
|
|
58
|
+
return asyncio.run(db_manager.load_setting(key))
|
|
270
59
|
|
|
271
|
-
cl_data._data_layer =
|
|
60
|
+
cl_data._data_layer = db_manager
|
|
272
61
|
|
|
273
62
|
tavily_api_key = os.getenv("TAVILY_API_KEY")
|
|
274
63
|
tavily_client = TavilyClient(api_key=tavily_api_key) if tavily_api_key else None
|
|
@@ -323,6 +112,37 @@ tools = [{
|
|
|
323
112
|
}
|
|
324
113
|
}] if tavily_api_key else []
|
|
325
114
|
|
|
115
|
+
# Authentication configuration
|
|
116
|
+
AUTH_PASSWORD_ENABLED = os.getenv("AUTH_PASSWORD_ENABLED", "true").lower() == "true" # Password authentication enabled by default
|
|
117
|
+
AUTH_OAUTH_ENABLED = os.getenv("AUTH_OAUTH_ENABLED", "false").lower() == "true" # OAuth authentication disabled by default
|
|
118
|
+
|
|
119
|
+
username = os.getenv("CHAINLIT_USERNAME", "admin")
|
|
120
|
+
password = os.getenv("CHAINLIT_PASSWORD", "admin")
|
|
121
|
+
|
|
122
|
+
def auth_callback(u: str, p: str):
|
|
123
|
+
if (u, p) == (username, password):
|
|
124
|
+
return cl.User(identifier=username, metadata={"role": "ADMIN", "provider": "credentials"})
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
def oauth_callback(
|
|
128
|
+
provider_id: str,
|
|
129
|
+
token: str,
|
|
130
|
+
raw_user_data: Dict[str, str],
|
|
131
|
+
default_user: cl.User,
|
|
132
|
+
) -> Optional[cl.User]:
|
|
133
|
+
return default_user
|
|
134
|
+
|
|
135
|
+
if AUTH_PASSWORD_ENABLED:
|
|
136
|
+
auth_callback = cl.password_auth_callback(auth_callback)
|
|
137
|
+
|
|
138
|
+
if AUTH_OAUTH_ENABLED:
|
|
139
|
+
oauth_callback = cl.oauth_callback(oauth_callback)
|
|
140
|
+
|
|
141
|
+
async def send_count():
|
|
142
|
+
await cl.Message(
|
|
143
|
+
f"Create step counter: {create_step_counter}", disable_feedback=True
|
|
144
|
+
).send()
|
|
145
|
+
|
|
326
146
|
@cl.on_chat_start
|
|
327
147
|
async def start():
|
|
328
148
|
model_name = load_setting("model_name") or os.getenv("MODEL_NAME", "gpt-4o-mini")
|
|
@@ -358,7 +178,7 @@ async def setup_agent(settings):
|
|
|
358
178
|
if isinstance(metadata, str):
|
|
359
179
|
try:
|
|
360
180
|
metadata = json.loads(metadata)
|
|
361
|
-
except:
|
|
181
|
+
except json.JSONDecodeError:
|
|
362
182
|
metadata = {}
|
|
363
183
|
metadata["model_name"] = model_name
|
|
364
184
|
await cl_data.update_thread(thread_id, metadata=metadata)
|
|
@@ -516,23 +336,6 @@ User Question: {message.content}
|
|
|
516
336
|
msg.content = full_response
|
|
517
337
|
await msg.update()
|
|
518
338
|
|
|
519
|
-
username = os.getenv("CHAINLIT_USERNAME", "admin")
|
|
520
|
-
password = os.getenv("CHAINLIT_PASSWORD", "admin")
|
|
521
|
-
|
|
522
|
-
@cl.password_auth_callback
|
|
523
|
-
def auth_callback(u: str, p: str):
|
|
524
|
-
if (u, p) == (username, password):
|
|
525
|
-
return cl.User(
|
|
526
|
-
identifier=username, metadata={"role": "ADMIN", "provider": "credentials"}
|
|
527
|
-
)
|
|
528
|
-
else:
|
|
529
|
-
return None
|
|
530
|
-
|
|
531
|
-
async def send_count():
|
|
532
|
-
await cl.Message(
|
|
533
|
-
f"Create step counter: {create_step_counter}", disable_feedback=True
|
|
534
|
-
).send()
|
|
535
|
-
|
|
536
339
|
@cl.on_chat_resume
|
|
537
340
|
async def on_chat_resume(thread: ThreadDict):
|
|
538
341
|
logger.info(f"Resuming chat: {thread['id']}")
|
|
@@ -1,23 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
from chainlit.input_widget import TextInput
|
|
3
|
-
from chainlit.types import ThreadDict, StepDict
|
|
1
|
+
# Standard library imports
|
|
4
2
|
import os
|
|
5
|
-
import sqlite3
|
|
6
3
|
from datetime import datetime
|
|
7
|
-
from typing import Dict, List, Optional
|
|
8
|
-
from dotenv import load_dotenv
|
|
9
|
-
load_dotenv()
|
|
10
|
-
import chainlit.data as cl_data
|
|
11
4
|
import logging
|
|
12
5
|
import json
|
|
13
|
-
|
|
6
|
+
import io
|
|
7
|
+
import base64
|
|
8
|
+
import asyncio
|
|
9
|
+
|
|
10
|
+
# Third-party imports
|
|
11
|
+
from dotenv import load_dotenv
|
|
12
|
+
from PIL import Image
|
|
14
13
|
from context import ContextGatherer
|
|
15
14
|
from tavily import TavilyClient
|
|
16
|
-
from datetime import datetime
|
|
17
15
|
from crawl4ai import AsyncWebCrawler
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
import
|
|
16
|
+
|
|
17
|
+
# Local application/library imports
|
|
18
|
+
import chainlit as cl
|
|
19
|
+
from chainlit.input_widget import TextInput
|
|
20
|
+
from chainlit.types import ThreadDict
|
|
21
|
+
import chainlit.data as cl_data
|
|
22
|
+
from litellm import acompletion
|
|
23
|
+
from db import DatabaseManager
|
|
24
|
+
|
|
25
|
+
# Load environment variables
|
|
26
|
+
load_dotenv()
|
|
21
27
|
|
|
22
28
|
# Set up logging
|
|
23
29
|
logger = logging.getLogger(__name__)
|
|
@@ -41,143 +47,38 @@ if not CHAINLIT_AUTH_SECRET:
|
|
|
41
47
|
CHAINLIT_AUTH_SECRET = os.getenv("CHAINLIT_AUTH_SECRET")
|
|
42
48
|
|
|
43
49
|
now = datetime.now()
|
|
44
|
-
|
|
45
50
|
create_step_counter = 0
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
cursor = conn.cursor()
|
|
53
|
-
cursor.execute('''
|
|
54
|
-
CREATE TABLE IF NOT EXISTS users (
|
|
55
|
-
id UUID PRIMARY KEY,
|
|
56
|
-
identifier TEXT NOT NULL UNIQUE,
|
|
57
|
-
metadata JSONB NOT NULL,
|
|
58
|
-
createdAt TEXT
|
|
59
|
-
)
|
|
60
|
-
''')
|
|
61
|
-
cursor.execute('''
|
|
62
|
-
CREATE TABLE IF NOT EXISTS threads (
|
|
63
|
-
id UUID PRIMARY KEY,
|
|
64
|
-
createdAt TEXT,
|
|
65
|
-
name TEXT,
|
|
66
|
-
userId UUID,
|
|
67
|
-
userIdentifier TEXT,
|
|
68
|
-
tags TEXT[],
|
|
69
|
-
metadata JSONB NOT NULL DEFAULT '{}',
|
|
70
|
-
FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE
|
|
71
|
-
)
|
|
72
|
-
''')
|
|
73
|
-
cursor.execute('''
|
|
74
|
-
CREATE TABLE IF NOT EXISTS steps (
|
|
75
|
-
id UUID PRIMARY KEY,
|
|
76
|
-
name TEXT NOT NULL,
|
|
77
|
-
type TEXT NOT NULL,
|
|
78
|
-
threadId UUID NOT NULL,
|
|
79
|
-
parentId UUID,
|
|
80
|
-
disableFeedback BOOLEAN NOT NULL DEFAULT 0,
|
|
81
|
-
streaming BOOLEAN NOT NULL DEFAULT 0,
|
|
82
|
-
waitForAnswer BOOLEAN DEFAULT 0,
|
|
83
|
-
isError BOOLEAN NOT NULL DEFAULT 0,
|
|
84
|
-
metadata JSONB DEFAULT '{}',
|
|
85
|
-
tags TEXT[],
|
|
86
|
-
input TEXT,
|
|
87
|
-
output TEXT,
|
|
88
|
-
createdAt TEXT,
|
|
89
|
-
start TEXT,
|
|
90
|
-
end TEXT,
|
|
91
|
-
generation JSONB,
|
|
92
|
-
showInput TEXT,
|
|
93
|
-
language TEXT,
|
|
94
|
-
indent INT,
|
|
95
|
-
FOREIGN KEY (threadId) REFERENCES threads (id) ON DELETE CASCADE
|
|
96
|
-
)
|
|
97
|
-
''')
|
|
98
|
-
cursor.execute('''
|
|
99
|
-
CREATE TABLE IF NOT EXISTS elements (
|
|
100
|
-
id UUID PRIMARY KEY,
|
|
101
|
-
threadId UUID,
|
|
102
|
-
type TEXT,
|
|
103
|
-
url TEXT,
|
|
104
|
-
chainlitKey TEXT,
|
|
105
|
-
name TEXT NOT NULL,
|
|
106
|
-
display TEXT,
|
|
107
|
-
objectKey TEXT,
|
|
108
|
-
size TEXT,
|
|
109
|
-
page INT,
|
|
110
|
-
language TEXT,
|
|
111
|
-
forId UUID,
|
|
112
|
-
mime TEXT,
|
|
113
|
-
FOREIGN KEY (threadId) REFERENCES threads (id) ON DELETE CASCADE
|
|
114
|
-
)
|
|
115
|
-
''')
|
|
116
|
-
cursor.execute('''
|
|
117
|
-
CREATE TABLE IF NOT EXISTS feedbacks (
|
|
118
|
-
id UUID PRIMARY KEY,
|
|
119
|
-
forId UUID NOT NULL,
|
|
120
|
-
value INT NOT NULL,
|
|
121
|
-
threadId UUID,
|
|
122
|
-
comment TEXT
|
|
123
|
-
)
|
|
124
|
-
''')
|
|
125
|
-
cursor.execute('''
|
|
126
|
-
CREATE TABLE IF NOT EXISTS settings (
|
|
127
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
128
|
-
key TEXT UNIQUE,
|
|
129
|
-
value TEXT
|
|
130
|
-
)
|
|
131
|
-
''')
|
|
132
|
-
conn.commit()
|
|
133
|
-
conn.close()
|
|
52
|
+
# Initialize database
|
|
53
|
+
db_manager = DatabaseManager()
|
|
54
|
+
db_manager.initialize()
|
|
55
|
+
|
|
56
|
+
deleted_thread_ids = [] # type: List[str]
|
|
134
57
|
|
|
135
58
|
def save_setting(key: str, value: str):
|
|
136
59
|
"""Saves a setting to the database.
|
|
137
|
-
|
|
60
|
+
|
|
138
61
|
Args:
|
|
139
62
|
key: The setting key.
|
|
140
63
|
value: The setting value.
|
|
141
64
|
"""
|
|
142
|
-
|
|
143
|
-
cursor = conn.cursor()
|
|
144
|
-
cursor.execute(
|
|
145
|
-
"""
|
|
146
|
-
INSERT OR REPLACE INTO settings (id, key, value)
|
|
147
|
-
VALUES ((SELECT id FROM settings WHERE key = ?), ?, ?)
|
|
148
|
-
""",
|
|
149
|
-
(key, key, value),
|
|
150
|
-
)
|
|
151
|
-
conn.commit()
|
|
152
|
-
conn.close()
|
|
65
|
+
asyncio.run(db_manager.save_setting(key, value))
|
|
153
66
|
|
|
154
67
|
def load_setting(key: str) -> str:
|
|
155
68
|
"""Loads a setting from the database.
|
|
156
|
-
|
|
69
|
+
|
|
157
70
|
Args:
|
|
158
71
|
key: The setting key.
|
|
159
|
-
|
|
72
|
+
|
|
160
73
|
Returns:
|
|
161
74
|
The setting value, or None if the key is not found.
|
|
162
75
|
"""
|
|
163
|
-
|
|
164
|
-
cursor = conn.cursor()
|
|
165
|
-
cursor.execute('SELECT value FROM settings WHERE key = ?', (key,))
|
|
166
|
-
result = cursor.fetchone()
|
|
167
|
-
conn.close()
|
|
168
|
-
return result[0] if result else None
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
# Initialize the database
|
|
172
|
-
initialize_db()
|
|
173
|
-
|
|
174
|
-
deleted_thread_ids = [] # type: List[str]
|
|
76
|
+
return asyncio.run(db_manager.load_setting(key))
|
|
175
77
|
|
|
176
|
-
cl_data._data_layer =
|
|
78
|
+
cl_data._data_layer = db_manager
|
|
177
79
|
|
|
178
80
|
@cl.on_chat_start
|
|
179
81
|
async def start():
|
|
180
|
-
initialize_db()
|
|
181
82
|
model_name = load_setting("model_name")
|
|
182
83
|
|
|
183
84
|
if (model_name):
|
|
@@ -498,7 +399,6 @@ async def on_chat_resume(thread: ThreadDict):
|
|
|
498
399
|
]
|
|
499
400
|
)
|
|
500
401
|
await settings.send()
|
|
501
|
-
thread_id = thread["id"]
|
|
502
402
|
cl.user_session.set("thread_id", thread["id"])
|
|
503
403
|
|
|
504
404
|
# Ensure metadata is a dictionary
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sqlite3
|
|
3
|
+
import asyncio
|
|
4
|
+
from sqlalchemy import text
|
|
5
|
+
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
|
6
|
+
from sqlalchemy.orm import sessionmaker
|
|
7
|
+
from sql_alchemy import SQLAlchemyDataLayer
|
|
8
|
+
import chainlit.data as cl_data
|
|
9
|
+
from chainlit.types import ThreadDict
|
|
10
|
+
|
|
11
|
+
class DatabaseManager(SQLAlchemyDataLayer):
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self.database_url = os.getenv("DATABASE_URL")
|
|
14
|
+
supabase_url = os.getenv("SUPABASE_DATABASE_URL")
|
|
15
|
+
if supabase_url:
|
|
16
|
+
self.database_url = supabase_url
|
|
17
|
+
|
|
18
|
+
if self.database_url:
|
|
19
|
+
self.conninfo = self.database_url
|
|
20
|
+
else:
|
|
21
|
+
self.db_path = os.path.expanduser("~/.praison/database.sqlite")
|
|
22
|
+
os.makedirs(os.path.dirname(self.db_path), exist_ok=True)
|
|
23
|
+
self.conninfo = f"sqlite+aiosqlite:///{self.db_path}"
|
|
24
|
+
|
|
25
|
+
# Initialize SQLAlchemyDataLayer with the connection info
|
|
26
|
+
super().__init__(conninfo=self.conninfo)
|
|
27
|
+
|
|
28
|
+
async def create_schema_async(self):
|
|
29
|
+
"""Create the database schema for PostgreSQL"""
|
|
30
|
+
if not self.database_url:
|
|
31
|
+
return
|
|
32
|
+
engine = create_async_engine(self.database_url, echo=False)
|
|
33
|
+
async with engine.begin() as conn:
|
|
34
|
+
await conn.execute(text('''
|
|
35
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
36
|
+
"id" TEXT PRIMARY KEY,
|
|
37
|
+
"identifier" TEXT NOT NULL UNIQUE,
|
|
38
|
+
"meta" TEXT NOT NULL DEFAULT '{}',
|
|
39
|
+
"createdAt" TEXT
|
|
40
|
+
);
|
|
41
|
+
'''))
|
|
42
|
+
await conn.execute(text('''
|
|
43
|
+
CREATE TABLE IF NOT EXISTS threads (
|
|
44
|
+
"id" TEXT PRIMARY KEY,
|
|
45
|
+
"createdAt" TEXT,
|
|
46
|
+
"name" TEXT,
|
|
47
|
+
"userId" TEXT,
|
|
48
|
+
"userIdentifier" TEXT,
|
|
49
|
+
"tags" TEXT DEFAULT '[]',
|
|
50
|
+
"meta" TEXT NOT NULL DEFAULT '{}',
|
|
51
|
+
FOREIGN KEY ("userId") REFERENCES users("id") ON DELETE CASCADE
|
|
52
|
+
);
|
|
53
|
+
'''))
|
|
54
|
+
await conn.execute(text('''
|
|
55
|
+
CREATE TABLE IF NOT EXISTS steps (
|
|
56
|
+
"id" TEXT PRIMARY KEY,
|
|
57
|
+
"name" TEXT NOT NULL,
|
|
58
|
+
"type" TEXT NOT NULL,
|
|
59
|
+
"threadId" TEXT NOT NULL,
|
|
60
|
+
"parentId" TEXT,
|
|
61
|
+
"disableFeedback" BOOLEAN NOT NULL DEFAULT FALSE,
|
|
62
|
+
"streaming" BOOLEAN NOT NULL DEFAULT FALSE,
|
|
63
|
+
"waitForAnswer" BOOLEAN DEFAULT FALSE,
|
|
64
|
+
"isError" BOOLEAN NOT NULL DEFAULT FALSE,
|
|
65
|
+
"meta" TEXT DEFAULT '{}',
|
|
66
|
+
"tags" TEXT DEFAULT '[]',
|
|
67
|
+
"input" TEXT,
|
|
68
|
+
"output" TEXT,
|
|
69
|
+
"createdAt" TEXT,
|
|
70
|
+
"startTime" TEXT,
|
|
71
|
+
"endTime" TEXT,
|
|
72
|
+
"generation" TEXT,
|
|
73
|
+
"showInput" TEXT,
|
|
74
|
+
"language" TEXT,
|
|
75
|
+
"indent" INT,
|
|
76
|
+
FOREIGN KEY ("threadId") REFERENCES threads("id") ON DELETE CASCADE
|
|
77
|
+
);
|
|
78
|
+
'''))
|
|
79
|
+
await conn.execute(text('''
|
|
80
|
+
CREATE TABLE IF NOT EXISTS elements (
|
|
81
|
+
"id" TEXT PRIMARY KEY,
|
|
82
|
+
"threadId" TEXT,
|
|
83
|
+
"type" TEXT,
|
|
84
|
+
"url" TEXT,
|
|
85
|
+
"chainlitKey" TEXT,
|
|
86
|
+
"name" TEXT NOT NULL,
|
|
87
|
+
"display" TEXT,
|
|
88
|
+
"objectKey" TEXT,
|
|
89
|
+
"size" TEXT,
|
|
90
|
+
"page" INT,
|
|
91
|
+
"language" TEXT,
|
|
92
|
+
"forId" TEXT,
|
|
93
|
+
"mime" TEXT,
|
|
94
|
+
FOREIGN KEY ("threadId") REFERENCES threads("id") ON DELETE CASCADE
|
|
95
|
+
);
|
|
96
|
+
'''))
|
|
97
|
+
await conn.execute(text('''
|
|
98
|
+
CREATE TABLE IF NOT EXISTS feedbacks (
|
|
99
|
+
"id" TEXT PRIMARY KEY,
|
|
100
|
+
"forId" TEXT NOT NULL,
|
|
101
|
+
"value" INT NOT NULL,
|
|
102
|
+
"threadId" TEXT,
|
|
103
|
+
"comment" TEXT
|
|
104
|
+
);
|
|
105
|
+
'''))
|
|
106
|
+
await conn.execute(text('''
|
|
107
|
+
CREATE TABLE IF NOT EXISTS settings (
|
|
108
|
+
"id" SERIAL PRIMARY KEY,
|
|
109
|
+
"key" TEXT UNIQUE,
|
|
110
|
+
"value" TEXT
|
|
111
|
+
);
|
|
112
|
+
'''))
|
|
113
|
+
await engine.dispose()
|
|
114
|
+
|
|
115
|
+
def create_schema_sqlite(self):
|
|
116
|
+
"""Create the database schema for SQLite"""
|
|
117
|
+
conn = sqlite3.connect(self.db_path)
|
|
118
|
+
cursor = conn.cursor()
|
|
119
|
+
cursor.execute('''
|
|
120
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
121
|
+
id TEXT PRIMARY KEY,
|
|
122
|
+
identifier TEXT NOT NULL UNIQUE,
|
|
123
|
+
meta TEXT NOT NULL DEFAULT '{}',
|
|
124
|
+
createdAt TEXT
|
|
125
|
+
);
|
|
126
|
+
''')
|
|
127
|
+
cursor.execute('''
|
|
128
|
+
CREATE TABLE IF NOT EXISTS threads (
|
|
129
|
+
id TEXT PRIMARY KEY,
|
|
130
|
+
createdAt TEXT,
|
|
131
|
+
name TEXT,
|
|
132
|
+
userId TEXT,
|
|
133
|
+
userIdentifier TEXT,
|
|
134
|
+
tags TEXT DEFAULT '[]',
|
|
135
|
+
meta TEXT NOT NULL DEFAULT '{}',
|
|
136
|
+
FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE
|
|
137
|
+
);
|
|
138
|
+
''')
|
|
139
|
+
cursor.execute('''
|
|
140
|
+
CREATE TABLE IF NOT EXISTS steps (
|
|
141
|
+
id TEXT PRIMARY KEY,
|
|
142
|
+
name TEXT NOT NULL,
|
|
143
|
+
type TEXT NOT NULL,
|
|
144
|
+
threadId TEXT NOT NULL,
|
|
145
|
+
parentId TEXT,
|
|
146
|
+
disableFeedback BOOLEAN NOT NULL DEFAULT 0,
|
|
147
|
+
streaming BOOLEAN NOT NULL DEFAULT 0,
|
|
148
|
+
waitForAnswer BOOLEAN DEFAULT 0,
|
|
149
|
+
isError BOOLEAN NOT NULL DEFAULT 0,
|
|
150
|
+
meta TEXT DEFAULT '{}',
|
|
151
|
+
tags TEXT DEFAULT '[]',
|
|
152
|
+
input TEXT,
|
|
153
|
+
output TEXT,
|
|
154
|
+
createdAt TEXT,
|
|
155
|
+
startTime TEXT,
|
|
156
|
+
endTime TEXT,
|
|
157
|
+
generation TEXT,
|
|
158
|
+
showInput TEXT,
|
|
159
|
+
language TEXT,
|
|
160
|
+
indent INT,
|
|
161
|
+
FOREIGN KEY (threadId) REFERENCES threads(id) ON DELETE CASCADE
|
|
162
|
+
);
|
|
163
|
+
''')
|
|
164
|
+
cursor.execute('''
|
|
165
|
+
CREATE TABLE IF NOT EXISTS elements (
|
|
166
|
+
id TEXT PRIMARY KEY,
|
|
167
|
+
threadId TEXT,
|
|
168
|
+
type TEXT,
|
|
169
|
+
url TEXT,
|
|
170
|
+
chainlitKey TEXT,
|
|
171
|
+
name TEXT NOT NULL,
|
|
172
|
+
display TEXT,
|
|
173
|
+
objectKey TEXT,
|
|
174
|
+
size TEXT,
|
|
175
|
+
page INT,
|
|
176
|
+
language TEXT,
|
|
177
|
+
forId TEXT,
|
|
178
|
+
mime TEXT,
|
|
179
|
+
FOREIGN KEY (threadId) REFERENCES threads(id) ON DELETE CASCADE
|
|
180
|
+
);
|
|
181
|
+
''')
|
|
182
|
+
cursor.execute('''
|
|
183
|
+
CREATE TABLE IF NOT EXISTS feedbacks (
|
|
184
|
+
id TEXT PRIMARY KEY,
|
|
185
|
+
forId TEXT NOT NULL,
|
|
186
|
+
value INT NOT NULL,
|
|
187
|
+
threadId TEXT,
|
|
188
|
+
comment TEXT
|
|
189
|
+
);
|
|
190
|
+
''')
|
|
191
|
+
cursor.execute('''
|
|
192
|
+
CREATE TABLE IF NOT EXISTS settings (
|
|
193
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
194
|
+
key TEXT UNIQUE,
|
|
195
|
+
value TEXT
|
|
196
|
+
);
|
|
197
|
+
''')
|
|
198
|
+
conn.commit()
|
|
199
|
+
conn.close()
|
|
200
|
+
|
|
201
|
+
def initialize(self):
|
|
202
|
+
"""Initialize the database with schema based on the configuration"""
|
|
203
|
+
if self.database_url:
|
|
204
|
+
asyncio.run(self.create_schema_async())
|
|
205
|
+
else:
|
|
206
|
+
self.create_schema_sqlite()
|
|
207
|
+
|
|
208
|
+
async def save_setting(self, key: str, value: str):
|
|
209
|
+
"""Save a setting to the database"""
|
|
210
|
+
if self.database_url:
|
|
211
|
+
async with self.engine.begin() as conn:
|
|
212
|
+
await conn.execute(text("""
|
|
213
|
+
INSERT INTO settings ("key", "value") VALUES (:key, :value)
|
|
214
|
+
ON CONFLICT ("key") DO UPDATE SET "value" = EXCLUDED."value"
|
|
215
|
+
"""), {"key": key, "value": value})
|
|
216
|
+
else:
|
|
217
|
+
conn = sqlite3.connect(self.db_path)
|
|
218
|
+
cursor = conn.cursor()
|
|
219
|
+
cursor.execute(
|
|
220
|
+
"""
|
|
221
|
+
INSERT OR REPLACE INTO settings (id, key, value)
|
|
222
|
+
VALUES ((SELECT id FROM settings WHERE key = ?), ?, ?)
|
|
223
|
+
""",
|
|
224
|
+
(key, key, value),
|
|
225
|
+
)
|
|
226
|
+
conn.commit()
|
|
227
|
+
conn.close()
|
|
228
|
+
|
|
229
|
+
async def load_setting(self, key: str) -> str:
|
|
230
|
+
"""Load a setting from the database"""
|
|
231
|
+
if self.database_url:
|
|
232
|
+
async with self.engine.connect() as conn:
|
|
233
|
+
result = await conn.execute(text('SELECT "value" FROM settings WHERE "key" = :key'), {"key": key})
|
|
234
|
+
row = result.fetchone()
|
|
235
|
+
return row[0] if row else None
|
|
236
|
+
else:
|
|
237
|
+
conn = sqlite3.connect(self.db_path)
|
|
238
|
+
cursor = conn.cursor()
|
|
239
|
+
cursor.execute('SELECT value FROM settings WHERE key = ?', (key,))
|
|
240
|
+
result = cursor.fetchone()
|
|
241
|
+
conn.close()
|
|
242
|
+
return result[0] if result else None
|
|
@@ -34,6 +34,10 @@ if TYPE_CHECKING:
|
|
|
34
34
|
from chainlit.step import StepDict
|
|
35
35
|
|
|
36
36
|
DATABASE_URL = os.getenv("DATABASE_URL")
|
|
37
|
+
SUPABASE_DATABASE_URL = os.getenv("SUPABASE_DATABASE_URL")
|
|
38
|
+
if SUPABASE_DATABASE_URL:
|
|
39
|
+
# If a Supabase database URL is provided, use it.
|
|
40
|
+
DATABASE_URL = SUPABASE_DATABASE_URL
|
|
37
41
|
|
|
38
42
|
class SQLAlchemyDataLayer(BaseDataLayer):
|
|
39
43
|
def __init__(
|
|
@@ -262,7 +266,7 @@ class SQLAlchemyDataLayer(BaseDataLayer):
|
|
|
262
266
|
)
|
|
263
267
|
if not filters.userId:
|
|
264
268
|
raise ValueError("userId is required")
|
|
265
|
-
all_user_threads: List[ThreadDict] = (
|
|
269
|
+
all_user_threads: Optional[List[ThreadDict]] = (
|
|
266
270
|
await self.get_all_user_threads(user_id=filters.userId) or []
|
|
267
271
|
)
|
|
268
272
|
|
|
@@ -324,8 +328,6 @@ class SQLAlchemyDataLayer(BaseDataLayer):
|
|
|
324
328
|
else None
|
|
325
329
|
)
|
|
326
330
|
|
|
327
|
-
# Use updated column names: startTime, endTime
|
|
328
|
-
# Make sure tags is always a JSON array
|
|
329
331
|
tags = step_dict.get("tags")
|
|
330
332
|
if not tags:
|
|
331
333
|
tags = []
|
|
@@ -1,6 +1,16 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "PraisonAI"
|
|
3
|
+
version = "1.0.8"
|
|
4
|
+
description = "PraisonAI application combines AutoGen and CrewAI or similar frameworks into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customization, and efficient human-agent collaboration."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = ""
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Mervin Praison" }
|
|
9
|
+
]
|
|
10
|
+
|
|
1
11
|
[tool.poetry]
|
|
2
12
|
name = "PraisonAI"
|
|
3
|
-
version = "1.0.
|
|
13
|
+
version = "1.0.8"
|
|
4
14
|
description = "PraisonAI application combines AutoGen and CrewAI or similar frameworks into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customization, and efficient human-agent collaboration."
|
|
5
15
|
authors = ["Mervin Praison"]
|
|
6
16
|
license = ""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|