PraisonAI 0.0.46__py3-none-any.whl → 0.0.48__py3-none-any.whl

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/cli.py CHANGED
@@ -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
  """
praisonai/deploy.py CHANGED
@@ -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")
praisonai/ui/chat.py CHANGED
@@ -12,6 +12,8 @@ import chainlit.data as cl_data
12
12
  from chainlit.step import StepDict
13
13
  from literalai.helper import utc_now
14
14
  import logging
15
+ import json
16
+ from sql_alchemy import SQLAlchemyDataLayer
15
17
 
16
18
  # Set up logging
17
19
  logger = logging.getLogger(__name__)
@@ -38,31 +40,81 @@ now = utc_now()
38
40
 
39
41
  create_step_counter = 0
40
42
 
41
- import json
42
-
43
43
  DB_PATH = "threads.db"
44
44
 
45
45
  def initialize_db():
46
46
  conn = sqlite3.connect(DB_PATH)
47
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
+ ''')
48
56
  cursor.execute('''
49
57
  CREATE TABLE IF NOT EXISTS threads (
50
- id TEXT PRIMARY KEY,
51
- name TEXT,
58
+ id UUID PRIMARY KEY,
52
59
  createdAt TEXT,
53
- userId TEXT,
54
- userIdentifier 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
55
66
  )
56
67
  ''')
57
68
  cursor.execute('''
58
69
  CREATE TABLE IF NOT EXISTS steps (
59
- id TEXT PRIMARY KEY,
60
- threadId TEXT,
61
- name TEXT,
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,
62
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,
63
97
  type TEXT,
64
- output TEXT,
65
- FOREIGN KEY (threadId) REFERENCES threads (id)
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
66
118
  )
67
119
  ''')
68
120
  cursor.execute('''
@@ -110,210 +162,13 @@ def load_setting(key: str) -> str:
110
162
  conn.close()
111
163
  return result[0] if result else None
112
164
 
113
- def save_thread_to_db(thread):
114
- conn = sqlite3.connect(DB_PATH)
115
- cursor = conn.cursor()
116
- cursor.execute('''
117
- INSERT OR REPLACE INTO threads (id, name, createdAt, userId, userIdentifier)
118
- VALUES (?, ?, ?, ?, ?)
119
- ''', (thread['id'], thread['name'], thread['createdAt'], thread['userId'], thread['userIdentifier']))
120
-
121
- # No steps to save as steps are empty in the provided thread data
122
- conn.commit()
123
- conn.close()
124
- logger.debug("Thread saved to DB")
125
-
126
- def update_thread_in_db(thread):
127
- conn = sqlite3.connect(DB_PATH)
128
- cursor = conn.cursor()
129
-
130
- # Insert or update the thread
131
- cursor.execute('''
132
- INSERT OR REPLACE INTO threads (id, name, createdAt, userId, userIdentifier)
133
- VALUES (?, ?, ?, ?, ?)
134
- ''', (thread['id'], thread['name'], thread['createdAt'], thread['userId'], thread['userIdentifier']))
135
-
136
- # Fetch message_history from metadata
137
- message_history = cl.user_session.get("message_history", [])
138
-
139
- # Ensure user messages come first followed by assistant messages
140
- user_messages = [msg for msg in message_history if msg['role'] == 'user']
141
- assistant_messages = [msg for msg in message_history if msg['role'] == 'assistant']
142
- ordered_steps = [val for pair in zip(user_messages, assistant_messages) for val in pair]
143
-
144
- # Generate steps from ordered message_history
145
- steps = []
146
- for idx, message in enumerate(ordered_steps):
147
- step_id = f"{thread['id']}-step-{idx}"
148
- step_type = 'user_message' if message['role'] == 'user' else 'assistant_message'
149
- step_name = 'user' if message['role'] == 'user' else 'assistant'
150
- created_at = message.get('createdAt', thread['createdAt']) # Use thread's createdAt if no timestamp in message
151
- steps.append({
152
- 'id': step_id,
153
- 'threadId': thread['id'],
154
- 'name': step_name,
155
- 'createdAt': created_at,
156
- 'type': step_type,
157
- 'output': message['content']
158
- })
159
-
160
- # Insert all steps into the database
161
- for step in steps:
162
- cursor.execute('''
163
- INSERT OR REPLACE INTO steps (id, threadId, name, createdAt, type, output)
164
- VALUES (?, ?, ?, ?, ?, ?)
165
- ''', (step['id'], step['threadId'], step['name'], step['createdAt'], step['type'], step['output']))
166
-
167
- conn.commit()
168
- conn.close()
169
- logger.debug("Thread updated in DB")
170
-
171
- def delete_thread_from_db(thread_id: str):
172
- """Deletes a thread and its steps from the database.
173
-
174
- Args:
175
- thread_id: The ID of the thread to delete.
176
- """
177
- conn = sqlite3.connect(DB_PATH)
178
- cursor = conn.cursor()
179
- cursor.execute('DELETE FROM threads WHERE id = ?', (thread_id,))
180
- cursor.execute('DELETE FROM steps WHERE threadId = ?', (thread_id,))
181
- conn.commit()
182
- conn.close()
183
-
184
- def load_threads_from_db():
185
- conn = sqlite3.connect(DB_PATH)
186
- cursor = conn.cursor()
187
- cursor.execute('SELECT * FROM threads ORDER BY createdAt ASC')
188
- thread_rows = cursor.fetchall()
189
- threads = []
190
- for thread_row in thread_rows:
191
- cursor.execute('SELECT * FROM steps WHERE threadId = ? ORDER BY createdAt ASC', (thread_row[0],))
192
- step_rows = cursor.fetchall()
193
- steps = []
194
- for step_row in step_rows:
195
- steps.append({
196
- "id": step_row[0],
197
- "threadId": step_row[1],
198
- "name": step_row[2],
199
- "createdAt": step_row[3],
200
- "type": step_row[4],
201
- "output": step_row[5]
202
- })
203
- threads.append({
204
- "id": thread_row[0],
205
- "name": thread_row[1],
206
- "createdAt": thread_row[2],
207
- "userId": thread_row[3],
208
- "userIdentifier": thread_row[4],
209
- "steps": steps
210
- })
211
- conn.close()
212
- logger.debug("Threads loaded from DB")
213
- return threads
214
165
 
215
166
  # Initialize the database
216
167
  initialize_db()
217
- thread_history = load_threads_from_db()
218
168
 
219
169
  deleted_thread_ids = [] # type: List[str]
220
170
 
221
- class TestDataLayer(cl_data.BaseDataLayer): # Implement SQLAlchemyDataLayer
222
- async def get_user(self, identifier: str):
223
- logger.debug(f"Getting user: {identifier}")
224
- return cl.PersistedUser(id="test", createdAt=now, identifier=identifier)
225
-
226
- async def create_user(self, user: cl.User):
227
- logger.debug(f"Creating user: {user.identifier}")
228
- return cl.PersistedUser(id="test", createdAt=now, identifier=user.identifier)
229
-
230
- async def update_thread(
231
- self,
232
- thread_id: str,
233
- name: Optional[str] = None,
234
- user_id: Optional[str] = None,
235
- metadata: Optional[Dict] = None,
236
- tags: Optional[List[str]] = None,
237
- ):
238
- logger.debug(f"Updating thread: {thread_id}")
239
- thread = next((t for t in thread_history if t["id"] == thread_id), None)
240
- if thread:
241
- if name:
242
- thread["name"] = name
243
- if metadata:
244
- thread["metadata"] = metadata
245
- if tags:
246
- thread["tags"] = tags
247
-
248
- logger.debug(f"Thread: {thread}")
249
- cl.user_session.set("message_history", thread['metadata']['message_history'])
250
- cl.user_session.set("thread_id", thread["id"])
251
- update_thread_in_db(thread)
252
- logger.debug(f"Thread updated: {thread_id}")
253
-
254
- else:
255
- thread_history.append(
256
- {
257
- "id": thread_id,
258
- "name": name,
259
- "metadata": metadata,
260
- "tags": tags,
261
- "createdAt": utc_now(),
262
- "userId": user_id,
263
- "userIdentifier": "admin",
264
- "steps": [],
265
- }
266
- )
267
- thread = {
268
- "id": thread_id,
269
- "name": name,
270
- "metadata": metadata,
271
- "tags": tags,
272
- "createdAt": utc_now(),
273
- "userId": user_id,
274
- "userIdentifier": "admin",
275
- "steps": [],
276
- }
277
- save_thread_to_db(thread)
278
- logger.debug(f"Thread created: {thread_id}")
279
-
280
- @cl_data.queue_until_user_message()
281
- async def create_step(self, step_dict: StepDict):
282
- global create_step_counter
283
- create_step_counter += 1
284
-
285
- thread = next(
286
- (t for t in thread_history if t["id"] == step_dict.get("threadId")), None
287
- )
288
- if thread:
289
- thread["steps"].append(step_dict)
290
-
291
- async def get_thread_author(self, thread_id: str):
292
- logger.debug(f"Getting thread author: {thread_id}")
293
- return "admin"
294
-
295
- async def list_threads(
296
- self, pagination: cl_data.Pagination, filters: cl_data.ThreadFilter
297
- ) -> cl_data.PaginatedResponse[cl_data.ThreadDict]:
298
- logger.debug(f"Listing threads")
299
- return cl_data.PaginatedResponse(
300
- data=[t for t in thread_history if t["id"] not in deleted_thread_ids][::-1],
301
- pageInfo=cl_data.PageInfo(
302
- hasNextPage=False, startCursor=None, endCursor=None
303
- ),
304
- )
305
-
306
- async def get_thread(self, thread_id: str):
307
- logger.debug(f"Getting thread: {thread_id}")
308
- thread_history = load_threads_from_db()
309
- return next((t for t in thread_history if t["id"] == thread_id), None)
310
-
311
- async def delete_thread(self, thread_id: str):
312
- deleted_thread_ids.append(thread_id)
313
- delete_thread_from_db(thread_id)
314
- logger.debug(f"Deleted thread: {thread_id}")
315
-
316
- cl_data._data_layer = TestDataLayer()
171
+ cl_data._data_layer = SQLAlchemyDataLayer(conninfo=f"sqlite+aiosqlite:///{DB_PATH}")
317
172
 
318
173
  @cl.on_chat_start
319
174
  async def start():
@@ -357,7 +212,12 @@ async def setup_agent(settings):
357
212
  if thread:
358
213
  metadata = thread.get("metadata", {})
359
214
  metadata["model_name"] = model_name
360
- await cl_data.update_thread(thread_id, metadata=metadata)
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)
361
221
 
362
222
  @cl.on_message
363
223
  async def main(message: cl.Message):
@@ -372,9 +232,9 @@ async def main(message: cl.Message):
372
232
  model=model_name,
373
233
  messages=message_history,
374
234
  stream=True,
375
- temperature=0.7,
376
- max_tokens=500,
377
- top_p=1
235
+ # temperature=0.7,
236
+ # max_tokens=500,
237
+ # top_p=1
378
238
  )
379
239
 
380
240
  full_response = ""
@@ -423,6 +283,11 @@ async def on_chat_resume(thread: cl_data.ThreadDict):
423
283
  await settings.send()
424
284
  thread_id = thread["id"]
425
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
+
426
291
  message_history = cl.user_session.get("message_history", [])
427
292
  steps = thread["steps"]
428
293
 
@@ -435,4 +300,4 @@ async def on_chat_resume(thread: cl_data.ThreadDict):
435
300
  else:
436
301
  logger.warning(f"Message without type: {message}")
437
302
 
438
- cl.user_session.set("message_history", message_history)
303
+ cl.user_session.set("message_history", message_history)