PraisonAI 0.0.46__tar.gz → 0.0.48__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.

Files changed (40) hide show
  1. {praisonai-0.0.46 → praisonai-0.0.48}/PKG-INFO +8 -7
  2. {praisonai-0.0.46 → praisonai-0.0.48}/README.md +4 -4
  3. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/cli.py +35 -0
  4. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/deploy.py +3 -3
  5. praisonai-0.0.48/praisonai/ui/chat.py +303 -0
  6. praisonai-0.0.48/praisonai/ui/code.py +317 -0
  7. praisonai-0.0.48/praisonai/ui/context.py +140 -0
  8. praisonai-0.0.48/praisonai/ui/sql_alchemy.py +638 -0
  9. {praisonai-0.0.46 → praisonai-0.0.48}/pyproject.toml +3 -2
  10. praisonai-0.0.46/praisonai/ui/chat.py +0 -438
  11. {praisonai-0.0.46 → praisonai-0.0.48}/LICENSE +0 -0
  12. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/__init__.py +0 -0
  13. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/__main__.py +0 -0
  14. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/agents_generator.py +0 -0
  15. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/auto.py +0 -0
  16. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/chainlit_ui.py +0 -0
  17. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/inbuilt_tools/__init__.py +0 -0
  18. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/inbuilt_tools/autogen_tools.py +0 -0
  19. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/inc/__init__.py +0 -0
  20. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/inc/models.py +0 -0
  21. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/public/android-chrome-192x192.png +0 -0
  22. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/public/android-chrome-512x512.png +0 -0
  23. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/public/apple-touch-icon.png +0 -0
  24. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/public/fantasy.svg +0 -0
  25. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/public/favicon-16x16.png +0 -0
  26. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/public/favicon-32x32.png +0 -0
  27. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/public/favicon.ico +0 -0
  28. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/public/game.svg +0 -0
  29. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/public/logo_dark.png +0 -0
  30. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/public/logo_light.png +0 -0
  31. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/public/movie.svg +0 -0
  32. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/public/thriller.svg +0 -0
  33. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/test.py +0 -0
  34. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/ui/public/fantasy.svg +0 -0
  35. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/ui/public/game.svg +0 -0
  36. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/ui/public/logo_dark.png +0 -0
  37. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/ui/public/logo_light.png +0 -0
  38. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/ui/public/movie.svg +0 -0
  39. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/ui/public/thriller.svg +0 -0
  40. {praisonai-0.0.46 → praisonai-0.0.48}/praisonai/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PraisonAI
3
- Version: 0.0.46
3
+ Version: 0.0.48
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
@@ -12,13 +12,14 @@ Provides-Extra: agentops
12
12
  Provides-Extra: anthropic
13
13
  Provides-Extra: api
14
14
  Provides-Extra: chat
15
+ Provides-Extra: code
15
16
  Provides-Extra: cohere
16
17
  Provides-Extra: google
17
18
  Provides-Extra: gradio
18
19
  Provides-Extra: openai
19
20
  Provides-Extra: ui
20
21
  Requires-Dist: agentops (>=0.2.6) ; extra == "agentops"
21
- Requires-Dist: chainlit (>=1.1.301,<2.0.0) ; extra == "ui" or extra == "chat"
22
+ Requires-Dist: chainlit (>=1.1.301,<2.0.0) ; extra == "ui" or extra == "chat" or extra == "code"
22
23
  Requires-Dist: crewai (>=0.32.0)
23
24
  Requires-Dist: flask (>=3.0.0) ; extra == "api"
24
25
  Requires-Dist: gradio (>=4.26.0) ; extra == "gradio"
@@ -26,7 +27,7 @@ Requires-Dist: langchain-anthropic (>=0.1.13) ; extra == "anthropic"
26
27
  Requires-Dist: langchain-cohere (>=0.1.4) ; extra == "cohere"
27
28
  Requires-Dist: langchain-google-genai (>=1.0.4) ; extra == "google"
28
29
  Requires-Dist: langchain-openai (>=0.1.7) ; extra == "openai"
29
- Requires-Dist: litellm (>=1.41.8) ; extra == "chat"
30
+ Requires-Dist: litellm (>=1.41.8) ; extra == "chat" or extra == "code"
30
31
  Requires-Dist: markdown (>=3.5)
31
32
  Requires-Dist: praisonai-tools (>=0.0.7)
32
33
  Requires-Dist: pyautogen (>=0.2.19)
@@ -255,10 +256,10 @@ roles:
255
256
  """
256
257
 
257
258
  # Create a PraisonAI instance with the agent_yaml content
258
- praison_ai = PraisonAI(agent_yaml=agent_yaml)
259
+ praisonai = PraisonAI(agent_yaml=agent_yaml)
259
260
 
260
261
  # Run PraisonAI
261
- result = praison_ai.main()
262
+ result = praisonai.run()
262
263
 
263
264
  # Print the result
264
265
  print(result)
@@ -273,8 +274,8 @@ Note: Please create agents.yaml file before hand.
273
274
  from praisonai import PraisonAI
274
275
 
275
276
  def basic(): # Basic Mode
276
- praison_ai = PraisonAI(agent_file="agents.yaml")
277
- praison_ai.main()
277
+ praisonai = PraisonAI(agent_file="agents.yaml")
278
+ praisonai.run()
278
279
 
279
280
  if __name__ == "__main__":
280
281
  basic()
@@ -217,10 +217,10 @@ roles:
217
217
  """
218
218
 
219
219
  # Create a PraisonAI instance with the agent_yaml content
220
- praison_ai = PraisonAI(agent_yaml=agent_yaml)
220
+ praisonai = PraisonAI(agent_yaml=agent_yaml)
221
221
 
222
222
  # Run PraisonAI
223
- result = praison_ai.main()
223
+ result = praisonai.run()
224
224
 
225
225
  # Print the result
226
226
  print(result)
@@ -235,8 +235,8 @@ Note: Please create agents.yaml file before hand.
235
235
  from praisonai import PraisonAI
236
236
 
237
237
  def basic(): # Basic Mode
238
- praison_ai = PraisonAI(agent_file="agents.yaml")
239
- praison_ai.main()
238
+ praisonai = PraisonAI(agent_file="agents.yaml")
239
+ praisonai.run()
240
240
 
241
241
  if __name__ == "__main__":
242
242
  basic()
@@ -95,6 +95,10 @@ class PraisonAI:
95
95
  self.create_chainlit_chat_interface()
96
96
  return
97
97
 
98
+ if getattr(args, 'code', False):
99
+ self.create_code_interface()
100
+ return
101
+
98
102
  invocation_cmd = "praisonai"
99
103
  version_string = f"PraisonAI version {__version__}"
100
104
 
@@ -177,6 +181,9 @@ class PraisonAI:
177
181
  if args.agent_file == 'chat':
178
182
  args.ui = 'chainlit'
179
183
  args.chat = True
184
+ if args.agent_file == 'code':
185
+ args.ui = 'chainlit'
186
+ args.code = True
180
187
 
181
188
  return args
182
189
 
@@ -207,6 +214,34 @@ class PraisonAI:
207
214
  chainlit_run([chat_ui_path])
208
215
  else:
209
216
  print("ERROR: Chat UI is not installed. Please install it with 'pip install \"praisonai\[chat]\"' to use the chat UI.")
217
+
218
+ def create_code_interface(self):
219
+ """
220
+ Create a Chainlit interface for the code application.
221
+
222
+ This function sets up a Chainlit application that listens for messages.
223
+ When a message is received, it runs PraisonAI with the provided message as the topic.
224
+ The generated agents are then used to perform tasks.
225
+
226
+ Returns:
227
+ None: This function does not return any value. It starts the Chainlit application.
228
+ """
229
+ if CHAINLIT_AVAILABLE:
230
+ import praisonai
231
+ os.environ["CHAINLIT_PORT"] = "8086"
232
+ public_folder = os.path.join(os.path.dirname(praisonai.__file__), 'public')
233
+ if not os.path.exists("public"): # Check if the folder exists in the current directory
234
+ if os.path.exists(public_folder):
235
+ shutil.copytree(public_folder, 'public', dirs_exist_ok=True)
236
+ logging.info("Public folder copied successfully!")
237
+ else:
238
+ logging.info("Public folder not found in the package.")
239
+ else:
240
+ logging.info("Public folder already exists.")
241
+ code_ui_path = os.path.join(os.path.dirname(praisonai.__file__), 'ui', 'code.py')
242
+ chainlit_run([code_ui_path])
243
+ else:
244
+ print("ERROR: Code UI is not installed. Please install it with 'pip install \"praisonai\[code]\"' to use the code UI.")
210
245
 
211
246
  def create_gradio_interface(self):
212
247
  """
@@ -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==0.0.46 gunicorn markdown\n")
59
+ file.write("RUN pip install flask praisonai==0.0.48 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
 
@@ -78,8 +78,8 @@ class CloudDeployer:
78
78
  file.write("import markdown\n\n")
79
79
  file.write("app = Flask(__name__)\n\n")
80
80
  file.write("def basic():\n")
81
- file.write(" praison_ai = PraisonAI(agent_file=\"agents.yaml\")\n")
82
- file.write(" return praison_ai.main()\n\n")
81
+ file.write(" praisonai = PraisonAI(agent_file=\"agents.yaml\")\n")
82
+ file.write(" return praisonai.run()\n\n")
83
83
  file.write("@app.route('/')\n")
84
84
  file.write("def home():\n")
85
85
  file.write(" output = basic()\n")
@@ -0,0 +1,303 @@
1
+ import chainlit as cl
2
+ from chainlit.input_widget import TextInput
3
+ from chainlit.types import ThreadDict
4
+ from litellm import acompletion
5
+ import os
6
+ import sqlite3
7
+ from datetime import datetime
8
+ from typing import Dict, List, Optional
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
14
+ import logging
15
+ import json
16
+ from sql_alchemy import SQLAlchemyDataLayer
17
+
18
+ # Set up logging
19
+ logger = logging.getLogger(__name__)
20
+ log_level = os.getenv("LOGLEVEL", "INFO").upper()
21
+ logger.handlers = []
22
+
23
+ # Set up logging to console
24
+ console_handler = logging.StreamHandler()
25
+ console_handler.setLevel(log_level)
26
+ console_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
27
+ console_handler.setFormatter(console_formatter)
28
+ logger.addHandler(console_handler)
29
+
30
+ # Set the logging level for the logger
31
+ logger.setLevel(log_level)
32
+
33
+ CHAINLIT_AUTH_SECRET = os.getenv("CHAINLIT_AUTH_SECRET")
34
+
35
+ if not CHAINLIT_AUTH_SECRET:
36
+ os.environ["CHAINLIT_AUTH_SECRET"] = "p8BPhQChpg@J>jBz$wGxqLX2V>yTVgP*7Ky9H$aV:axW~ANNX-7_T:o@lnyCBu^U"
37
+ CHAINLIT_AUTH_SECRET = os.getenv("CHAINLIT_AUTH_SECRET")
38
+
39
+ now = utc_now()
40
+
41
+ create_step_counter = 0
42
+
43
+ DB_PATH = "threads.db"
44
+
45
+ def initialize_db():
46
+ conn = sqlite3.connect(DB_PATH)
47
+ cursor = conn.cursor()
48
+ cursor.execute('''
49
+ CREATE TABLE IF NOT EXISTS users (
50
+ id UUID PRIMARY KEY,
51
+ identifier TEXT NOT NULL UNIQUE,
52
+ metadata JSONB NOT NULL,
53
+ createdAt TEXT
54
+ )
55
+ ''')
56
+ cursor.execute('''
57
+ CREATE TABLE IF NOT EXISTS threads (
58
+ id UUID PRIMARY KEY,
59
+ createdAt TEXT,
60
+ name TEXT,
61
+ userId UUID,
62
+ userIdentifier TEXT,
63
+ tags TEXT[],
64
+ metadata JSONB NOT NULL DEFAULT '{}',
65
+ FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE
66
+ )
67
+ ''')
68
+ cursor.execute('''
69
+ CREATE TABLE IF NOT EXISTS steps (
70
+ id UUID PRIMARY KEY,
71
+ name TEXT NOT NULL,
72
+ type TEXT NOT NULL,
73
+ threadId UUID NOT NULL,
74
+ parentId UUID,
75
+ disableFeedback BOOLEAN NOT NULL,
76
+ streaming BOOLEAN NOT NULL,
77
+ waitForAnswer BOOLEAN,
78
+ isError BOOLEAN,
79
+ metadata JSONB,
80
+ tags TEXT[],
81
+ input TEXT,
82
+ output TEXT,
83
+ createdAt TEXT,
84
+ start TEXT,
85
+ end TEXT,
86
+ generation JSONB,
87
+ showInput TEXT,
88
+ language TEXT,
89
+ indent INT,
90
+ FOREIGN KEY (threadId) REFERENCES threads (id) ON DELETE CASCADE
91
+ )
92
+ ''')
93
+ cursor.execute('''
94
+ CREATE TABLE IF NOT EXISTS elements (
95
+ id UUID PRIMARY KEY,
96
+ threadId UUID,
97
+ type TEXT,
98
+ url TEXT,
99
+ chainlitKey TEXT,
100
+ name TEXT NOT NULL,
101
+ display TEXT,
102
+ objectKey TEXT,
103
+ size TEXT,
104
+ page INT,
105
+ language TEXT,
106
+ forId UUID,
107
+ mime TEXT,
108
+ FOREIGN KEY (threadId) REFERENCES threads (id) ON DELETE CASCADE
109
+ )
110
+ ''')
111
+ cursor.execute('''
112
+ CREATE TABLE IF NOT EXISTS feedbacks (
113
+ id UUID PRIMARY KEY,
114
+ forId UUID NOT NULL,
115
+ value INT NOT NULL,
116
+ threadId UUID,
117
+ comment TEXT
118
+ )
119
+ ''')
120
+ cursor.execute('''
121
+ CREATE TABLE IF NOT EXISTS settings (
122
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
123
+ key TEXT UNIQUE,
124
+ value TEXT
125
+ )
126
+ ''')
127
+ conn.commit()
128
+ conn.close()
129
+
130
+ def save_setting(key: str, value: str):
131
+ """Saves a setting to the database.
132
+
133
+ Args:
134
+ key: The setting key.
135
+ value: The setting value.
136
+ """
137
+ conn = sqlite3.connect(DB_PATH)
138
+ cursor = conn.cursor()
139
+ cursor.execute(
140
+ """
141
+ INSERT OR REPLACE INTO settings (id, key, value)
142
+ VALUES ((SELECT id FROM settings WHERE key = ?), ?, ?)
143
+ """,
144
+ (key, key, value),
145
+ )
146
+ conn.commit()
147
+ conn.close()
148
+
149
+ def load_setting(key: str) -> str:
150
+ """Loads a setting from the database.
151
+
152
+ Args:
153
+ key: The setting key.
154
+
155
+ Returns:
156
+ The setting value, or None if the key is not found.
157
+ """
158
+ conn = sqlite3.connect(DB_PATH)
159
+ cursor = conn.cursor()
160
+ cursor.execute('SELECT value FROM settings WHERE key = ?', (key,))
161
+ result = cursor.fetchone()
162
+ conn.close()
163
+ return result[0] if result else None
164
+
165
+
166
+ # Initialize the database
167
+ initialize_db()
168
+
169
+ deleted_thread_ids = [] # type: List[str]
170
+
171
+ cl_data._data_layer = SQLAlchemyDataLayer(conninfo=f"sqlite+aiosqlite:///{DB_PATH}")
172
+
173
+ @cl.on_chat_start
174
+ async def start():
175
+ initialize_db()
176
+ model_name = load_setting("model_name")
177
+
178
+ if model_name:
179
+ cl.user_session.set("model_name", model_name)
180
+ else:
181
+ # If no setting found, use default or environment variable
182
+ model_name = os.getenv("MODEL_NAME", "gpt-3.5-turbo")
183
+ cl.user_session.set("model_name", model_name)
184
+ logger.debug(f"Model name: {model_name}")
185
+ settings = cl.ChatSettings(
186
+ [
187
+ TextInput(
188
+ id="model_name",
189
+ label="Enter the Model Name",
190
+ placeholder="e.g., gpt-3.5-turbo",
191
+ initial=model_name
192
+ )
193
+ ]
194
+ )
195
+ cl.user_session.set("settings", settings)
196
+ await settings.send()
197
+
198
+ @cl.on_settings_update
199
+ async def setup_agent(settings):
200
+ logger.debug(settings)
201
+ cl.user_session.set("settings", settings)
202
+ model_name = settings["model_name"]
203
+ cl.user_session.set("model_name", model_name)
204
+
205
+ # Save in settings table
206
+ save_setting("model_name", model_name)
207
+
208
+ # Save in thread metadata
209
+ thread_id = cl.user_session.get("thread_id")
210
+ if thread_id:
211
+ thread = await cl_data.get_thread(thread_id)
212
+ if thread:
213
+ metadata = thread.get("metadata", {})
214
+ metadata["model_name"] = model_name
215
+
216
+ # Always store metadata as a JSON string
217
+ await cl_data.update_thread(thread_id, metadata=json.dumps(metadata))
218
+
219
+ # Update the user session with the new metadata
220
+ cl.user_session.set("metadata", metadata)
221
+
222
+ @cl.on_message
223
+ async def main(message: cl.Message):
224
+ model_name = load_setting("model_name") or os.getenv("MODEL_NAME") or "gpt-3.5-turbo"
225
+ message_history = cl.user_session.get("message_history", [])
226
+ message_history.append({"role": "user", "content": message.content})
227
+
228
+ msg = cl.Message(content="")
229
+ await msg.send()
230
+
231
+ response = await acompletion(
232
+ model=model_name,
233
+ messages=message_history,
234
+ stream=True,
235
+ # temperature=0.7,
236
+ # max_tokens=500,
237
+ # top_p=1
238
+ )
239
+
240
+ full_response = ""
241
+ async for part in response:
242
+ if token := part['choices'][0]['delta']['content']:
243
+ await msg.stream_token(token)
244
+ full_response += token
245
+ logger.debug(f"Full response: {full_response}")
246
+ message_history.append({"role": "assistant", "content": full_response})
247
+ logger.debug(f"Message history: {message_history}")
248
+ cl.user_session.set("message_history", message_history)
249
+ await msg.update()
250
+
251
+ username = os.getenv("CHAINLIT_USERNAME", "admin") # Default to "admin" if not found
252
+ password = os.getenv("CHAINLIT_PASSWORD", "admin") # Default to "admin" if not found
253
+
254
+ @cl.password_auth_callback
255
+ def auth_callback(username: str, password: str):
256
+ if (username, password) == (username, password):
257
+ return cl.User(
258
+ identifier=username, metadata={"role": "ADMIN", "provider": "credentials"}
259
+ )
260
+ else:
261
+ return None
262
+
263
+ async def send_count():
264
+ await cl.Message(
265
+ f"Create step counter: {create_step_counter}", disable_feedback=True
266
+ ).send()
267
+
268
+ @cl.on_chat_resume
269
+ async def on_chat_resume(thread: cl_data.ThreadDict):
270
+ logger.info(f"Resuming chat: {thread['id']}")
271
+ model_name = load_setting("model_name") or os.getenv("MODEL_NAME") or "gpt-3.5-turbo"
272
+ logger.debug(f"Model name: {model_name}")
273
+ settings = cl.ChatSettings(
274
+ [
275
+ TextInput(
276
+ id="model_name",
277
+ label="Enter the Model Name",
278
+ placeholder="e.g., gpt-3.5-turbo",
279
+ initial=model_name
280
+ )
281
+ ]
282
+ )
283
+ await settings.send()
284
+ thread_id = thread["id"]
285
+ cl.user_session.set("thread_id", thread["id"])
286
+
287
+ # The metadata should now already be a dictionary
288
+ metadata = thread.get("metadata", {})
289
+ cl.user_session.set("metadata", metadata)
290
+
291
+ message_history = cl.user_session.get("message_history", [])
292
+ steps = thread["steps"]
293
+
294
+ for message in steps:
295
+ msg_type = message.get("type")
296
+ if msg_type == "user_message":
297
+ message_history.append({"role": "user", "content": message.get("output", "")})
298
+ elif msg_type == "assistant_message":
299
+ message_history.append({"role": "assistant", "content": message.get("output", "")})
300
+ else:
301
+ logger.warning(f"Message without type: {message}")
302
+
303
+ cl.user_session.set("message_history", message_history)