PraisonAI 1.0.1__tar.gz → 1.0.3__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.1 → praisonai-1.0.3}/PKG-INFO +6 -3
- {praisonai-1.0.1 → praisonai-1.0.3}/README.md +2 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/cli.py +9 -4
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/deploy.py +1 -1
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/ui/chat.py +266 -131
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/ui/code.py +2 -5
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/ui/sql_alchemy.py +28 -26
- {praisonai-1.0.1 → praisonai-1.0.3}/pyproject.toml +8 -3
- {praisonai-1.0.1 → praisonai-1.0.3}/LICENSE +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/__init__.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/__main__.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/agents_generator.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/api/call.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/auto.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/chainlit_ui.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/inbuilt_tools/__init__.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/inbuilt_tools/autogen_tools.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/inc/__init__.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/inc/config.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/inc/models.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/public/android-chrome-192x192.png +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/public/android-chrome-512x512.png +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/public/apple-touch-icon.png +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/public/fantasy.svg +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/public/favicon-16x16.png +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/public/favicon-32x32.png +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/public/favicon.ico +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/public/game.svg +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/public/logo_dark.png +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/public/logo_light.png +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/public/movie.svg +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/public/thriller.svg +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/setup/__init__.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/setup/build.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/setup/config.yaml +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/setup/post_install.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/setup/setup_conda_env.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/setup/setup_conda_env.sh +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/setup.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/test.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/train.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/ui/context.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/ui/public/fantasy.svg +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/ui/public/game.svg +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/ui/public/logo_dark.png +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/ui/public/logo_light.png +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/ui/public/movie.svg +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/ui/public/thriller.svg +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/ui/realtime.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/ui/realtimeclient/__init__.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/ui/realtimeclient/realtimedocs.txt +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/praisonai/ui/realtimeclient/tools.py +0 -0
- {praisonai-1.0.1 → praisonai-1.0.3}/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.3
|
|
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
|
|
@@ -34,7 +34,7 @@ Requires-Dist: flaml[automl] (>=2.3.1) ; extra == "call"
|
|
|
34
34
|
Requires-Dist: flask (>=3.0.0) ; extra == "api"
|
|
35
35
|
Requires-Dist: gradio (>=4.26.0) ; extra == "gradio"
|
|
36
36
|
Requires-Dist: greenlet (>=3.0.3) ; extra == "chat" or extra == "code" or extra == "realtime"
|
|
37
|
-
Requires-Dist: instructor (>=0.4.8) ; extra == "call" or extra == "crewai" or extra == "autogen"
|
|
37
|
+
Requires-Dist: instructor (>=0.4.8) ; extra == "chat" or extra == "call" or extra == "crewai" or extra == "autogen"
|
|
38
38
|
Requires-Dist: langchain-anthropic (>=0.1.13) ; extra == "anthropic"
|
|
39
39
|
Requires-Dist: langchain-cohere (>=0.1.4) ; extra == "cohere"
|
|
40
40
|
Requires-Dist: langchain-google-genai (>=1.0.4) ; extra == "google"
|
|
@@ -46,10 +46,11 @@ Requires-Dist: playwright (>=1.47.0) ; extra == "chat" or extra == "code"
|
|
|
46
46
|
Requires-Dist: plotly (>=5.24.0) ; extra == "realtime"
|
|
47
47
|
Requires-Dist: praisonai-tools (>=0.0.7) ; extra == "crewai" or extra == "autogen"
|
|
48
48
|
Requires-Dist: pyautogen (>=0.2.19) ; extra == "autogen"
|
|
49
|
+
Requires-Dist: pydantic (<=2.10.1) ; extra == "chat" or extra == "code"
|
|
49
50
|
Requires-Dist: pyngrok (>=1.4.0) ; extra == "call"
|
|
50
51
|
Requires-Dist: pyparsing (>=3.0.0)
|
|
51
52
|
Requires-Dist: python-dotenv (>=0.19.0) ; extra == "call"
|
|
52
|
-
Requires-Dist: rich (>=13.7) ; extra == "call"
|
|
53
|
+
Requires-Dist: rich (>=13.7) ; extra == "chat" or extra == "call"
|
|
53
54
|
Requires-Dist: sqlalchemy (>=2.0.36) ; extra == "chat" or extra == "code" or extra == "realtime"
|
|
54
55
|
Requires-Dist: tavily-python (==0.5.0) ; extra == "chat" or extra == "code" or extra == "realtime"
|
|
55
56
|
Requires-Dist: twilio (>=7.0.0) ; extra == "call"
|
|
@@ -78,6 +79,8 @@ Description-Content-Type: text/markdown
|
|
|
78
79
|
|
|
79
80
|
# Praison AI
|
|
80
81
|
|
|
82
|
+
<a href="https://trendshift.io/repositories/9130" target="_blank"><img src="https://trendshift.io/api/badge/repositories/9130" alt="MervinPraison%2FPraisonAI | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
|
83
|
+
|
|
81
84
|
</div>
|
|
82
85
|
|
|
83
86
|
Praison AI, leveraging both AutoGen and CrewAI or any other agent framework, represents a low-code, centralised framework designed to simplify the creation and orchestration of multi-agent systems for various LLM applications, emphasizing ease of use, customization, and human-agent interaction.
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
# Praison AI
|
|
18
18
|
|
|
19
|
+
<a href="https://trendshift.io/repositories/9130" target="_blank"><img src="https://trendshift.io/api/badge/repositories/9130" alt="MervinPraison%2FPraisonAI | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
|
20
|
+
|
|
19
21
|
</div>
|
|
20
22
|
|
|
21
23
|
Praison AI, leveraging both AutoGen and CrewAI or any other agent framework, represents a low-code, centralised framework designed to simplify the creation and orchestration of multi-agent systems for various LLM applications, emphasizing ease of use, customization, and human-agent interaction.
|
|
@@ -26,7 +26,9 @@ CREWAI_AVAILABLE = False
|
|
|
26
26
|
AUTOGEN_AVAILABLE = False
|
|
27
27
|
|
|
28
28
|
try:
|
|
29
|
-
|
|
29
|
+
# Set CHAINLIT_APP_ROOT only if it doesn't exist
|
|
30
|
+
if "CHAINLIT_APP_ROOT" not in os.environ:
|
|
31
|
+
os.environ["CHAINLIT_APP_ROOT"] = os.path.join(os.path.expanduser("~"), ".praison")
|
|
30
32
|
from chainlit.cli import chainlit_run
|
|
31
33
|
CHAINLIT_AVAILABLE = True
|
|
32
34
|
except ImportError:
|
|
@@ -389,7 +391,8 @@ class PraisonAI:
|
|
|
389
391
|
import praisonai
|
|
390
392
|
os.environ["CHAINLIT_PORT"] = "8084"
|
|
391
393
|
root_path = os.path.join(os.path.expanduser("~"), ".praison")
|
|
392
|
-
|
|
394
|
+
if "CHAINLIT_APP_ROOT" not in os.environ:
|
|
395
|
+
os.environ["CHAINLIT_APP_ROOT"] = root_path
|
|
393
396
|
public_folder = os.path.join(os.path.dirname(praisonai.__file__), 'public')
|
|
394
397
|
if not os.path.exists(os.path.join(root_path, "public")):
|
|
395
398
|
if os.path.exists(public_folder):
|
|
@@ -412,7 +415,8 @@ class PraisonAI:
|
|
|
412
415
|
import praisonai
|
|
413
416
|
os.environ["CHAINLIT_PORT"] = "8086"
|
|
414
417
|
root_path = os.path.join(os.path.expanduser("~"), ".praison")
|
|
415
|
-
|
|
418
|
+
if "CHAINLIT_APP_ROOT" not in os.environ:
|
|
419
|
+
os.environ["CHAINLIT_APP_ROOT"] = root_path
|
|
416
420
|
public_folder = os.path.join(os.path.dirname(__file__), 'public')
|
|
417
421
|
if not os.path.exists(os.path.join(root_path, "public")):
|
|
418
422
|
if os.path.exists(public_folder):
|
|
@@ -481,7 +485,8 @@ class PraisonAI:
|
|
|
481
485
|
import praisonai
|
|
482
486
|
os.environ["CHAINLIT_PORT"] = "8088"
|
|
483
487
|
root_path = os.path.join(os.path.expanduser("~"), ".praison")
|
|
484
|
-
|
|
488
|
+
if "CHAINLIT_APP_ROOT" not in os.environ:
|
|
489
|
+
os.environ["CHAINLIT_APP_ROOT"] = root_path
|
|
485
490
|
public_folder = os.path.join(os.path.dirname(praisonai.__file__), 'public')
|
|
486
491
|
if not os.path.exists(os.path.join(root_path, "public")):
|
|
487
492
|
if os.path.exists(public_folder):
|
|
@@ -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.3 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,6 +1,6 @@
|
|
|
1
1
|
import chainlit as cl
|
|
2
2
|
from chainlit.input_widget import TextInput
|
|
3
|
-
from chainlit.types import ThreadDict
|
|
3
|
+
from chainlit.types import ThreadDict
|
|
4
4
|
from litellm import acompletion
|
|
5
5
|
import os
|
|
6
6
|
import sqlite3
|
|
@@ -9,7 +9,6 @@ from typing import Dict, List, Optional
|
|
|
9
9
|
from dotenv import load_dotenv
|
|
10
10
|
load_dotenv()
|
|
11
11
|
import chainlit.data as cl_data
|
|
12
|
-
from chainlit.step import StepDict
|
|
13
12
|
from literalai.helper import utc_now
|
|
14
13
|
import logging
|
|
15
14
|
import json
|
|
@@ -20,6 +19,8 @@ import asyncio
|
|
|
20
19
|
from PIL import Image
|
|
21
20
|
import io
|
|
22
21
|
import base64
|
|
22
|
+
from sqlalchemy import text
|
|
23
|
+
from sqlalchemy.exc import DatabaseError
|
|
23
24
|
|
|
24
25
|
# Set up logging
|
|
25
26
|
logger = logging.getLogger(__name__)
|
|
@@ -48,134 +49,225 @@ create_step_counter = 0
|
|
|
48
49
|
|
|
49
50
|
DB_PATH = os.path.expanduser("~/.praison/database.sqlite")
|
|
50
51
|
|
|
51
|
-
def
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
52
|
+
async def create_tables(engine):
|
|
53
|
+
"""Create all necessary tables if they don't exist."""
|
|
54
|
+
try:
|
|
55
|
+
async with engine.begin() as conn:
|
|
56
|
+
# Check if we're using PostgreSQL
|
|
57
|
+
dialect = engine.dialect.name
|
|
58
|
+
if dialect == 'postgresql':
|
|
59
|
+
# Create tables with PostgreSQL-specific types
|
|
60
|
+
await conn.execute(text("""
|
|
61
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
62
|
+
id UUID PRIMARY KEY,
|
|
63
|
+
identifier TEXT NOT NULL UNIQUE,
|
|
64
|
+
metadata JSONB NOT NULL,
|
|
65
|
+
createdAt TEXT
|
|
66
|
+
)
|
|
67
|
+
"""))
|
|
68
|
+
|
|
69
|
+
await conn.execute(text("""
|
|
70
|
+
CREATE TABLE IF NOT EXISTS threads (
|
|
71
|
+
id UUID PRIMARY KEY,
|
|
72
|
+
createdAt TEXT,
|
|
73
|
+
name TEXT,
|
|
74
|
+
userId UUID,
|
|
75
|
+
userIdentifier TEXT,
|
|
76
|
+
tags TEXT[],
|
|
77
|
+
metadata JSONB NOT NULL DEFAULT '{}',
|
|
78
|
+
FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE
|
|
79
|
+
)
|
|
80
|
+
"""))
|
|
81
|
+
|
|
82
|
+
await conn.execute(text("""
|
|
83
|
+
CREATE TABLE IF NOT EXISTS steps (
|
|
84
|
+
id UUID PRIMARY KEY,
|
|
85
|
+
name TEXT NOT NULL,
|
|
86
|
+
type TEXT NOT NULL,
|
|
87
|
+
threadId UUID NOT NULL,
|
|
88
|
+
parentId UUID,
|
|
89
|
+
disableFeedback BOOLEAN NOT NULL DEFAULT 0,
|
|
90
|
+
streaming BOOLEAN NOT NULL DEFAULT 0,
|
|
91
|
+
waitForAnswer BOOLEAN DEFAULT 0,
|
|
92
|
+
isError BOOLEAN NOT NULL DEFAULT 0,
|
|
93
|
+
metadata JSONB DEFAULT '{}',
|
|
94
|
+
tags TEXT[],
|
|
95
|
+
input TEXT,
|
|
96
|
+
output TEXT,
|
|
97
|
+
createdAt TEXT,
|
|
98
|
+
start TEXT,
|
|
99
|
+
end TEXT,
|
|
100
|
+
generation JSONB,
|
|
101
|
+
showInput TEXT,
|
|
102
|
+
language TEXT,
|
|
103
|
+
indent INT,
|
|
104
|
+
FOREIGN KEY (threadId) REFERENCES threads (id) ON DELETE CASCADE
|
|
105
|
+
)
|
|
106
|
+
"""))
|
|
107
|
+
|
|
108
|
+
await conn.execute(text("""
|
|
109
|
+
CREATE TABLE IF NOT EXISTS elements (
|
|
110
|
+
id UUID PRIMARY KEY,
|
|
111
|
+
threadId UUID,
|
|
112
|
+
type TEXT,
|
|
113
|
+
url TEXT,
|
|
114
|
+
chainlitKey TEXT,
|
|
115
|
+
name TEXT NOT NULL,
|
|
116
|
+
display TEXT,
|
|
117
|
+
objectKey TEXT,
|
|
118
|
+
size TEXT,
|
|
119
|
+
page INT,
|
|
120
|
+
language TEXT,
|
|
121
|
+
forId UUID,
|
|
122
|
+
mime TEXT,
|
|
123
|
+
FOREIGN KEY (threadId) REFERENCES threads (id) ON DELETE CASCADE
|
|
124
|
+
)
|
|
125
|
+
"""))
|
|
126
|
+
|
|
127
|
+
await conn.execute(text("""
|
|
128
|
+
CREATE TABLE IF NOT EXISTS feedbacks (
|
|
129
|
+
id UUID PRIMARY KEY,
|
|
130
|
+
forId UUID NOT NULL,
|
|
131
|
+
value INT NOT NULL,
|
|
132
|
+
threadId UUID,
|
|
133
|
+
comment TEXT
|
|
134
|
+
)
|
|
135
|
+
"""))
|
|
136
|
+
|
|
137
|
+
await conn.execute(text("""
|
|
138
|
+
CREATE TABLE IF NOT EXISTS settings (
|
|
139
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
140
|
+
key TEXT UNIQUE,
|
|
141
|
+
value TEXT
|
|
142
|
+
)
|
|
143
|
+
"""))
|
|
144
|
+
|
|
145
|
+
# Create indexes
|
|
146
|
+
await conn.execute(text(
|
|
147
|
+
"CREATE INDEX IF NOT EXISTS idx_steps_threadId ON steps(threadId)"
|
|
148
|
+
))
|
|
149
|
+
await conn.execute(text(
|
|
150
|
+
"CREATE INDEX IF NOT EXISTS idx_threads_createdAt ON threads(createdAt DESC)"
|
|
151
|
+
))
|
|
152
|
+
else:
|
|
153
|
+
# SQLite tables (existing schema)
|
|
154
|
+
await conn.execute(text("""
|
|
155
|
+
CREATE TABLE IF NOT EXISTS users (
|
|
156
|
+
id TEXT PRIMARY KEY,
|
|
157
|
+
identifier TEXT NOT NULL UNIQUE,
|
|
158
|
+
metadata TEXT NOT NULL,
|
|
159
|
+
createdAt TEXT
|
|
160
|
+
)
|
|
161
|
+
"""))
|
|
162
|
+
|
|
163
|
+
await conn.execute(text("""
|
|
164
|
+
CREATE TABLE IF NOT EXISTS threads (
|
|
165
|
+
id TEXT PRIMARY KEY,
|
|
166
|
+
createdAt TEXT,
|
|
167
|
+
name TEXT,
|
|
168
|
+
userId TEXT,
|
|
169
|
+
userIdentifier TEXT,
|
|
170
|
+
tags TEXT,
|
|
171
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
172
|
+
FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE
|
|
173
|
+
)
|
|
174
|
+
"""))
|
|
175
|
+
|
|
176
|
+
await conn.execute(text("""
|
|
177
|
+
CREATE TABLE IF NOT EXISTS steps (
|
|
178
|
+
id TEXT PRIMARY KEY,
|
|
179
|
+
name TEXT NOT NULL,
|
|
180
|
+
type TEXT NOT NULL,
|
|
181
|
+
threadId TEXT NOT NULL,
|
|
182
|
+
parentId TEXT,
|
|
183
|
+
disableFeedback BOOLEAN NOT NULL DEFAULT 0,
|
|
184
|
+
streaming BOOLEAN NOT NULL DEFAULT 0,
|
|
185
|
+
waitForAnswer BOOLEAN DEFAULT 0,
|
|
186
|
+
isError BOOLEAN NOT NULL DEFAULT 0,
|
|
187
|
+
metadata TEXT DEFAULT '{}',
|
|
188
|
+
tags TEXT,
|
|
189
|
+
input TEXT,
|
|
190
|
+
output TEXT,
|
|
191
|
+
createdAt TEXT,
|
|
192
|
+
start TEXT,
|
|
193
|
+
end TEXT,
|
|
194
|
+
generation TEXT,
|
|
195
|
+
showInput TEXT,
|
|
196
|
+
language TEXT,
|
|
197
|
+
indent INT,
|
|
198
|
+
FOREIGN KEY (threadId) REFERENCES threads (id) ON DELETE CASCADE
|
|
199
|
+
)
|
|
200
|
+
"""))
|
|
201
|
+
|
|
202
|
+
await conn.execute(text("""
|
|
203
|
+
CREATE TABLE IF NOT EXISTS elements (
|
|
204
|
+
id TEXT PRIMARY KEY,
|
|
205
|
+
threadId TEXT,
|
|
206
|
+
type TEXT,
|
|
207
|
+
url TEXT,
|
|
208
|
+
chainlitKey TEXT,
|
|
209
|
+
name TEXT NOT NULL,
|
|
210
|
+
display TEXT,
|
|
211
|
+
objectKey TEXT,
|
|
212
|
+
size TEXT,
|
|
213
|
+
page INT,
|
|
214
|
+
language TEXT,
|
|
215
|
+
forId TEXT,
|
|
216
|
+
mime TEXT,
|
|
217
|
+
FOREIGN KEY (threadId) REFERENCES threads (id) ON DELETE CASCADE
|
|
218
|
+
)
|
|
219
|
+
"""))
|
|
220
|
+
|
|
221
|
+
await conn.execute(text("""
|
|
222
|
+
CREATE TABLE IF NOT EXISTS feedbacks (
|
|
223
|
+
id TEXT PRIMARY KEY,
|
|
224
|
+
forId TEXT NOT NULL,
|
|
225
|
+
value INT NOT NULL,
|
|
226
|
+
threadId TEXT,
|
|
227
|
+
comment TEXT
|
|
228
|
+
)
|
|
229
|
+
"""))
|
|
230
|
+
|
|
231
|
+
await conn.execute(text("""
|
|
232
|
+
CREATE TABLE IF NOT EXISTS settings (
|
|
233
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
234
|
+
key TEXT UNIQUE,
|
|
235
|
+
value TEXT
|
|
236
|
+
)
|
|
237
|
+
"""))
|
|
238
|
+
|
|
239
|
+
# Create indexes
|
|
240
|
+
await conn.execute(text(
|
|
241
|
+
"CREATE INDEX IF NOT EXISTS idx_steps_threadId ON steps(threadId)"
|
|
242
|
+
))
|
|
243
|
+
await conn.execute(text(
|
|
244
|
+
"CREATE INDEX IF NOT EXISTS idx_threads_createdAt ON threads(createdAt DESC)"
|
|
245
|
+
))
|
|
246
|
+
|
|
247
|
+
logger.info(f"Successfully created tables for {dialect} database")
|
|
248
|
+
except Exception as e:
|
|
249
|
+
logger.error(f"Error creating tables: {str(e)}")
|
|
250
|
+
raise DatabaseError(f"Failed to create database tables: {str(e)}")
|
|
172
251
|
|
|
173
252
|
# Initialize the database
|
|
174
|
-
initialize_db()
|
|
253
|
+
async def initialize_db():
|
|
254
|
+
await create_tables(cl_data._data_layer.engine)
|
|
175
255
|
|
|
176
|
-
|
|
256
|
+
# Get database connection string from environment variable or use SQLite as default
|
|
257
|
+
DB_URL = os.getenv("DATABASE_URL", f"sqlite+aiosqlite:///{DB_PATH}")
|
|
258
|
+
if DB_URL.startswith('postgresql'):
|
|
259
|
+
# Convert postgresql:// to postgresql+psycopg:// if needed
|
|
260
|
+
DB_URL = DB_URL.replace('postgresql://', 'postgresql+psycopg://')
|
|
177
261
|
|
|
178
|
-
|
|
262
|
+
# Initialize SQLAlchemy data layer
|
|
263
|
+
cl_data._data_layer = SQLAlchemyDataLayer(
|
|
264
|
+
conninfo=DB_URL,
|
|
265
|
+
ssl_require=bool(os.getenv("DATABASE_SSL", False))
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# Create tables if using PostgreSQL
|
|
269
|
+
if DB_URL.startswith('postgresql'):
|
|
270
|
+
asyncio.run(initialize_db())
|
|
179
271
|
|
|
180
272
|
# Set Tavily API key
|
|
181
273
|
tavily_api_key = os.getenv("TAVILY_API_KEY")
|
|
@@ -236,10 +328,53 @@ tools = [{
|
|
|
236
328
|
}
|
|
237
329
|
}] if tavily_api_key else []
|
|
238
330
|
|
|
331
|
+
async def save_setting(key: str, value: str):
|
|
332
|
+
"""Saves a setting to the database.
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
key: The setting key.
|
|
336
|
+
value: The setting value.
|
|
337
|
+
"""
|
|
338
|
+
try:
|
|
339
|
+
async with cl_data._data_layer.engine.begin() as conn:
|
|
340
|
+
await conn.execute(
|
|
341
|
+
text("""
|
|
342
|
+
INSERT INTO settings (key, value)
|
|
343
|
+
VALUES (:key, :value)
|
|
344
|
+
ON CONFLICT (key) DO UPDATE SET value = :value
|
|
345
|
+
"""),
|
|
346
|
+
{"key": key, "value": value}
|
|
347
|
+
)
|
|
348
|
+
logger.debug(f"Saved setting {key}={value}")
|
|
349
|
+
except Exception as e:
|
|
350
|
+
logger.error(f"Error saving setting {key}: {str(e)}")
|
|
351
|
+
raise DatabaseError(f"Failed to save setting: {str(e)}")
|
|
352
|
+
|
|
353
|
+
async def load_setting(key: str) -> str:
|
|
354
|
+
"""Loads a setting from the database.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
key: The setting key.
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
The setting value, or None if the key is not found.
|
|
361
|
+
"""
|
|
362
|
+
try:
|
|
363
|
+
async with cl_data._data_layer.engine.connect() as conn:
|
|
364
|
+
result = await conn.execute(
|
|
365
|
+
text("SELECT value FROM settings WHERE key = :key"),
|
|
366
|
+
{"key": key}
|
|
367
|
+
)
|
|
368
|
+
row = result.fetchone()
|
|
369
|
+
return row[0] if row else None
|
|
370
|
+
except Exception as e:
|
|
371
|
+
logger.error(f"Error loading setting {key}: {str(e)}")
|
|
372
|
+
return None
|
|
373
|
+
|
|
239
374
|
@cl.on_chat_start
|
|
240
375
|
async def start():
|
|
241
|
-
initialize_db()
|
|
242
|
-
model_name = load_setting("model_name")
|
|
376
|
+
await initialize_db()
|
|
377
|
+
model_name = await load_setting("model_name")
|
|
243
378
|
|
|
244
379
|
if model_name:
|
|
245
380
|
cl.user_session.set("model_name", model_name)
|
|
@@ -269,7 +404,7 @@ async def setup_agent(settings):
|
|
|
269
404
|
cl.user_session.set("model_name", model_name)
|
|
270
405
|
|
|
271
406
|
# Save in settings table
|
|
272
|
-
save_setting("model_name", model_name)
|
|
407
|
+
await save_setting("model_name", model_name)
|
|
273
408
|
|
|
274
409
|
# Save in thread metadata
|
|
275
410
|
thread_id = cl.user_session.get("thread_id")
|
|
@@ -287,7 +422,7 @@ async def setup_agent(settings):
|
|
|
287
422
|
|
|
288
423
|
@cl.on_message
|
|
289
424
|
async def main(message: cl.Message):
|
|
290
|
-
model_name = load_setting("model_name") or os.getenv("MODEL_NAME") or "gpt-4o-mini"
|
|
425
|
+
model_name = await load_setting("model_name") or os.getenv("MODEL_NAME") or "gpt-4o-mini"
|
|
291
426
|
message_history = cl.user_session.get("message_history", [])
|
|
292
427
|
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
293
428
|
|
|
@@ -469,7 +604,7 @@ async def send_count():
|
|
|
469
604
|
@cl.on_chat_resume
|
|
470
605
|
async def on_chat_resume(thread: ThreadDict):
|
|
471
606
|
logger.info(f"Resuming chat: {thread['id']}")
|
|
472
|
-
model_name = load_setting("model_name") or os.getenv("MODEL_NAME") or "gpt-4o-mini"
|
|
607
|
+
model_name = await load_setting("model_name") or os.getenv("MODEL_NAME") or "gpt-4o-mini"
|
|
473
608
|
logger.debug(f"Model name: {model_name}")
|
|
474
609
|
settings = cl.ChatSettings(
|
|
475
610
|
[
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import chainlit as cl
|
|
2
2
|
from chainlit.input_widget import TextInput
|
|
3
|
-
from chainlit.types import ThreadDict
|
|
4
|
-
from litellm import acompletion, completion
|
|
3
|
+
from chainlit.types import ThreadDict, StepDict
|
|
5
4
|
import os
|
|
6
5
|
import sqlite3
|
|
7
6
|
from datetime import datetime
|
|
@@ -9,8 +8,6 @@ from typing import Dict, List, Optional
|
|
|
9
8
|
from dotenv import load_dotenv
|
|
10
9
|
load_dotenv()
|
|
11
10
|
import chainlit.data as cl_data
|
|
12
|
-
from chainlit.step import StepDict
|
|
13
|
-
from literalai.helper import utc_now
|
|
14
11
|
import logging
|
|
15
12
|
import json
|
|
16
13
|
from sql_alchemy import SQLAlchemyDataLayer
|
|
@@ -43,7 +40,7 @@ if not CHAINLIT_AUTH_SECRET:
|
|
|
43
40
|
os.environ["CHAINLIT_AUTH_SECRET"] = "p8BPhQChpg@J>jBz$wGxqLX2V>yTVgP*7Ky9H$aV:axW~ANNX-7_T:o@lnyCBu^U"
|
|
44
41
|
CHAINLIT_AUTH_SECRET = os.getenv("CHAINLIT_AUTH_SECRET")
|
|
45
42
|
|
|
46
|
-
now =
|
|
43
|
+
now = datetime.now()
|
|
47
44
|
|
|
48
45
|
create_step_counter = 0
|
|
49
46
|
|
|
@@ -12,6 +12,7 @@ from chainlit.data.base import BaseDataLayer, BaseStorageClient
|
|
|
12
12
|
from chainlit.data.utils import queue_until_user_message
|
|
13
13
|
from chainlit.element import ElementDict
|
|
14
14
|
from chainlit.logger import logger
|
|
15
|
+
from chainlit.message import Message
|
|
15
16
|
from chainlit.step import StepDict
|
|
16
17
|
from chainlit.types import (
|
|
17
18
|
Feedback,
|
|
@@ -204,9 +205,10 @@ class SQLAlchemyDataLayer(BaseDataLayer):
|
|
|
204
205
|
async def get_thread(self, thread_id: str) -> Optional[ThreadDict]:
|
|
205
206
|
if self.show_logger:
|
|
206
207
|
logger.info(f"SQLAlchemy: get_thread, thread_id={thread_id}")
|
|
207
|
-
user_threads: Optional[List[ThreadDict]] =
|
|
208
|
-
thread_id=thread_id
|
|
208
|
+
user_threads: Optional[List[ThreadDict]] = (
|
|
209
|
+
await self.get_all_user_threads(thread_id=thread_id) or []
|
|
209
210
|
)
|
|
211
|
+
|
|
210
212
|
if user_threads:
|
|
211
213
|
return user_threads[0]
|
|
212
214
|
else:
|
|
@@ -335,7 +337,7 @@ class SQLAlchemyDataLayer(BaseDataLayer):
|
|
|
335
337
|
|
|
336
338
|
###### Steps ######
|
|
337
339
|
@queue_until_user_message()
|
|
338
|
-
async def create_step(self, step_dict:
|
|
340
|
+
async def create_step(self, step_dict: dict):
|
|
339
341
|
if self.show_logger:
|
|
340
342
|
logger.info(f"SQLAlchemy: create_step, step_id={step_dict.get('id')}")
|
|
341
343
|
|
|
@@ -365,7 +367,7 @@ class SQLAlchemyDataLayer(BaseDataLayer):
|
|
|
365
367
|
await self.execute_sql(query=query, parameters=parameters)
|
|
366
368
|
|
|
367
369
|
@queue_until_user_message()
|
|
368
|
-
async def update_step(self, step_dict:
|
|
370
|
+
async def update_step(self, step_dict: dict):
|
|
369
371
|
if self.show_logger:
|
|
370
372
|
logger.info(f"SQLAlchemy: update_step, step_id={step_dict.get('id')}")
|
|
371
373
|
await self.create_step(step_dict)
|
|
@@ -646,37 +648,37 @@ class SQLAlchemyDataLayer(BaseDataLayer):
|
|
|
646
648
|
value=step_feedback["feedback_value"],
|
|
647
649
|
comment=step_feedback.get("feedback_comment"),
|
|
648
650
|
)
|
|
649
|
-
step_dict =
|
|
650
|
-
id
|
|
651
|
-
name
|
|
652
|
-
type
|
|
653
|
-
threadId
|
|
654
|
-
parentId
|
|
655
|
-
streaming
|
|
656
|
-
waitForAnswer
|
|
657
|
-
isError
|
|
658
|
-
metadata
|
|
651
|
+
step_dict = {
|
|
652
|
+
"id": step_feedback["step_id"],
|
|
653
|
+
"name": step_feedback["step_name"],
|
|
654
|
+
"type": step_feedback["step_type"],
|
|
655
|
+
"threadId": thread_id,
|
|
656
|
+
"parentId": step_feedback.get("step_parentid"),
|
|
657
|
+
"streaming": step_feedback.get("step_streaming", False),
|
|
658
|
+
"waitForAnswer": step_feedback.get("step_waitforanswer"),
|
|
659
|
+
"isError": step_feedback.get("step_iserror"),
|
|
660
|
+
"metadata": (
|
|
659
661
|
step_feedback["step_metadata"]
|
|
660
662
|
if step_feedback.get("step_metadata") is not None
|
|
661
663
|
else {}
|
|
662
664
|
),
|
|
663
|
-
tags
|
|
664
|
-
input
|
|
665
|
+
"tags": step_feedback.get("step_tags"),
|
|
666
|
+
"input": (
|
|
665
667
|
step_feedback.get("step_input", "")
|
|
666
668
|
if step_feedback.get("step_showinput")
|
|
667
669
|
not in [None, "false"]
|
|
668
670
|
else ""
|
|
669
671
|
),
|
|
670
|
-
output
|
|
671
|
-
createdAt
|
|
672
|
-
start
|
|
673
|
-
end
|
|
674
|
-
generation
|
|
675
|
-
showInput
|
|
676
|
-
language
|
|
677
|
-
indent
|
|
678
|
-
feedback
|
|
679
|
-
|
|
672
|
+
"output": step_feedback.get("step_output", ""),
|
|
673
|
+
"createdAt": step_feedback.get("step_createdat"),
|
|
674
|
+
"start": step_feedback.get("step_start"),
|
|
675
|
+
"end": step_feedback.get("step_end"),
|
|
676
|
+
"generation": step_feedback.get("step_generation"),
|
|
677
|
+
"showInput": step_feedback.get("step_showinput"),
|
|
678
|
+
"language": step_feedback.get("step_language"),
|
|
679
|
+
"indent": step_feedback.get("step_indent"),
|
|
680
|
+
"feedback": feedback,
|
|
681
|
+
}
|
|
680
682
|
# Append the step to the steps list of the corresponding ThreadDict
|
|
681
683
|
thread_dicts[thread_id]["steps"].append(step_dict)
|
|
682
684
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "PraisonAI"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.3"
|
|
4
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
5
|
authors = ["Mervin Praison"]
|
|
6
6
|
license = ""
|
|
@@ -48,6 +48,7 @@ pyngrok = {version = ">=1.4.0", optional = true}
|
|
|
48
48
|
sqlalchemy = {version = ">=2.0.36", optional = true}
|
|
49
49
|
playwright = {version = ">=1.47.0", optional = true}
|
|
50
50
|
openai = {version = ">=1.54.0", optional = true}
|
|
51
|
+
pydantic = {version = "<=2.10.1", optional = true}
|
|
51
52
|
|
|
52
53
|
[tool.poetry.group.docs.dependencies]
|
|
53
54
|
mkdocs = "*"
|
|
@@ -125,7 +126,10 @@ chat = [
|
|
|
125
126
|
"tavily-python",
|
|
126
127
|
"crawl4ai",
|
|
127
128
|
"sqlalchemy",
|
|
128
|
-
"playwright"
|
|
129
|
+
"playwright",
|
|
130
|
+
"rich",
|
|
131
|
+
"instructor",
|
|
132
|
+
"pydantic"
|
|
129
133
|
]
|
|
130
134
|
code = [
|
|
131
135
|
"chainlit",
|
|
@@ -135,7 +139,8 @@ code = [
|
|
|
135
139
|
"tavily-python",
|
|
136
140
|
"crawl4ai",
|
|
137
141
|
"sqlalchemy",
|
|
138
|
-
"playwright"
|
|
142
|
+
"playwright",
|
|
143
|
+
"pydantic"
|
|
139
144
|
]
|
|
140
145
|
train = ["setup-conda-env"]
|
|
141
146
|
realtime = [
|
|
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
|