PraisonAI 0.0.45__py3-none-any.whl → 0.0.47__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
@@ -60,6 +60,12 @@ class PraisonAI:
60
60
  self.auto = auto
61
61
  self.init = init
62
62
 
63
+ def run(self):
64
+ """
65
+ Run the PraisonAI application.
66
+ """
67
+ self.main()
68
+
63
69
  def main(self):
64
70
  """
65
71
  The main function of the PraisonAI object. It parses the command-line arguments,
@@ -188,6 +194,15 @@ class PraisonAI:
188
194
  if CHAINLIT_AVAILABLE:
189
195
  import praisonai
190
196
  os.environ["CHAINLIT_PORT"] = "8084"
197
+ public_folder = os.path.join(os.path.dirname(praisonai.__file__), 'public')
198
+ if not os.path.exists("public"): # Check if the folder exists in the current directory
199
+ if os.path.exists(public_folder):
200
+ shutil.copytree(public_folder, 'public', dirs_exist_ok=True)
201
+ logging.info("Public folder copied successfully!")
202
+ else:
203
+ logging.info("Public folder not found in the package.")
204
+ else:
205
+ logging.info("Public folder already exists.")
191
206
  chat_ui_path = os.path.join(os.path.dirname(praisonai.__file__), 'ui', 'chat.py')
192
207
  chainlit_run([chat_ui_path])
193
208
  else:
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.45 gunicorn markdown\n")
59
+ file.write("RUN pip install flask praisonai==0.0.47 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):
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)
@@ -0,0 +1,638 @@
1
+ import json
2
+ import ssl
3
+ import uuid
4
+ from dataclasses import asdict
5
+ from datetime import datetime
6
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
7
+
8
+ import aiofiles
9
+ import aiohttp
10
+ from chainlit.context import context
11
+ from chainlit.data import BaseDataLayer, BaseStorageClient, queue_until_user_message
12
+ from chainlit.element import ElementDict
13
+ from chainlit.logger import logger
14
+ from chainlit.step import StepDict
15
+ from chainlit.types import (
16
+ Feedback,
17
+ FeedbackDict,
18
+ PageInfo,
19
+ PaginatedResponse,
20
+ Pagination,
21
+ ThreadDict,
22
+ ThreadFilter,
23
+ )
24
+ from chainlit.user import PersistedUser, User
25
+ from sqlalchemy import text
26
+ from sqlalchemy.exc import SQLAlchemyError
27
+ from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine
28
+ from sqlalchemy.orm import sessionmaker
29
+ import chainlit as cl
30
+ from literalai.helper import utc_now
31
+ now = utc_now()
32
+
33
+ if TYPE_CHECKING:
34
+ from chainlit.element import Element, ElementDict
35
+ from chainlit.step import StepDict
36
+
37
+
38
+ class SQLAlchemyDataLayer(BaseDataLayer):
39
+ def __init__(
40
+ self,
41
+ conninfo: str,
42
+ ssl_require: bool = False,
43
+ storage_provider: Optional[BaseStorageClient] = None,
44
+ user_thread_limit: Optional[int] = 1000,
45
+ show_logger: Optional[bool] = False,
46
+ ):
47
+ self._conninfo = conninfo
48
+ self.user_thread_limit = user_thread_limit
49
+ self.show_logger = show_logger
50
+ ssl_args = {}
51
+ if ssl_require:
52
+ # Create an SSL context to require an SSL connection
53
+ ssl_context = ssl.create_default_context()
54
+ ssl_context.check_hostname = False
55
+ ssl_context.verify_mode = ssl.CERT_NONE
56
+ ssl_args["ssl"] = ssl_context
57
+ self.engine: AsyncEngine = create_async_engine(
58
+ self._conninfo, connect_args=ssl_args
59
+ )
60
+ self.async_session = sessionmaker(bind=self.engine, expire_on_commit=False, class_=AsyncSession) # type: ignore
61
+ if storage_provider:
62
+ self.storage_provider: Optional[BaseStorageClient] = storage_provider
63
+ if self.show_logger:
64
+ logger.info("SQLAlchemyDataLayer storage client initialized")
65
+ else:
66
+ self.storage_provider = None
67
+ logger.warn(
68
+ "SQLAlchemyDataLayer storage client is not initialized and elements will not be persisted!"
69
+ )
70
+
71
+ async def build_debug_url(self) -> str:
72
+ return ""
73
+
74
+ ###### SQL Helpers ######
75
+ async def execute_sql(
76
+ self, query: str, parameters: dict
77
+ ) -> Union[List[Dict[str, Any]], int, None]:
78
+ parameterized_query = text(query)
79
+ async with self.async_session() as session:
80
+ try:
81
+ await session.begin()
82
+ result = await session.execute(parameterized_query, parameters)
83
+ await session.commit()
84
+ if result.returns_rows:
85
+ json_result = [dict(row._mapping) for row in result.fetchall()]
86
+ clean_json_result = self.clean_result(json_result)
87
+ return clean_json_result
88
+ else:
89
+ return result.rowcount
90
+ except SQLAlchemyError as e:
91
+ await session.rollback()
92
+ logger.warn(f"An error occurred: {e}")
93
+ return None
94
+ except Exception as e:
95
+ await session.rollback()
96
+ logger.warn(f"An unexpected error occurred: {e}")
97
+ return None
98
+
99
+ async def get_current_timestamp(self) -> str:
100
+ return datetime.now().isoformat() + "Z"
101
+
102
+ def clean_result(self, obj):
103
+ """Recursively change UUID -> str and serialize dictionaries"""
104
+ if isinstance(obj, dict):
105
+ return {k: self.clean_result(v) for k, v in obj.items()}
106
+ elif isinstance(obj, list):
107
+ return [self.clean_result(item) for item in obj]
108
+ elif isinstance(obj, uuid.UUID):
109
+ return str(obj)
110
+ return obj
111
+
112
+ ###### User ######
113
+ async def get_user(self, identifier: str) -> Optional[PersistedUser]:
114
+ logger.debug(f"Getting user: {identifier}")
115
+ return cl.PersistedUser(id="test", createdAt=now, identifier=identifier)
116
+ if self.show_logger:
117
+ logger.info(f"SQLAlchemy: get_user, identifier={identifier}")
118
+ query = "SELECT * FROM users WHERE identifier = :identifier"
119
+ parameters = {"identifier": identifier}
120
+ result = await self.execute_sql(query=query, parameters=parameters)
121
+ if result and isinstance(result, list):
122
+ user_data = result[0]
123
+ return PersistedUser(**user_data)
124
+ return None
125
+
126
+ async def create_user(self, user: User) -> Optional[PersistedUser]:
127
+ logger.debug(f"Creating user: {user.identifier}")
128
+ return cl.PersistedUser(id="test", createdAt=now, identifier=user.identifier)
129
+ if self.show_logger:
130
+ logger.info(f"SQLAlchemy: create_user, user_identifier={user.identifier}")
131
+ existing_user: Optional["PersistedUser"] = await self.get_user(user.identifier)
132
+ user_dict: Dict[str, Any] = {
133
+ "identifier": str(user.identifier),
134
+ "metadata": json.dumps(user.metadata) or {},
135
+ }
136
+ if not existing_user: # create the user
137
+ if self.show_logger:
138
+ logger.info("SQLAlchemy: create_user, creating the user")
139
+ user_dict["id"] = str(uuid.uuid4())
140
+ user_dict["createdAt"] = await self.get_current_timestamp()
141
+ query = """INSERT INTO users ("id", "identifier", "createdAt", "metadata") VALUES (:id, :identifier, :createdAt, :metadata)"""
142
+ await self.execute_sql(query=query, parameters=user_dict)
143
+ else: # update the user
144
+ if self.show_logger:
145
+ logger.info("SQLAlchemy: update user metadata")
146
+ query = """UPDATE users SET "metadata" = :metadata WHERE "identifier" = :identifier"""
147
+ await self.execute_sql(
148
+ query=query, parameters=user_dict
149
+ ) # We want to update the metadata
150
+ return await self.get_user(user.identifier)
151
+
152
+ ###### Threads ######
153
+ async def get_thread_author(self, thread_id: str) -> str:
154
+ logger.debug(f"Getting thread author: {thread_id}")
155
+ return "admin"
156
+ if self.show_logger:
157
+ logger.info(f"SQLAlchemy: get_thread_author, thread_id={thread_id}")
158
+ query = """SELECT "userIdentifier" FROM threads WHERE "id" = :id"""
159
+ parameters = {"id": thread_id}
160
+ result = await self.execute_sql(query=query, parameters=parameters)
161
+ if isinstance(result, list) and result:
162
+ author_identifier = result[0].get("userIdentifier")
163
+ if author_identifier is not None:
164
+ return author_identifier
165
+ raise ValueError(f"Author not found for thread_id {thread_id}")
166
+
167
+ async def get_thread(self, thread_id: str) -> Optional[ThreadDict]:
168
+ if self.show_logger:
169
+ logger.info(f"SQLAlchemy: get_thread, thread_id={thread_id}")
170
+ user_threads: Optional[List[ThreadDict]] = await self.get_all_user_threads(
171
+ thread_id=thread_id
172
+ )
173
+ if user_threads:
174
+ thread = user_threads[0]
175
+ # Parse the metadata here
176
+ if isinstance(thread['metadata'], str):
177
+ try:
178
+ thread['metadata'] = json.loads(thread['metadata'])
179
+ except json.JSONDecodeError:
180
+ thread['metadata'] = {}
181
+ elif thread['metadata'] is None:
182
+ thread['metadata'] = {}
183
+ return thread
184
+ else:
185
+ return None
186
+
187
+ async def update_thread(
188
+ self,
189
+ thread_id: str,
190
+ name: Optional[str] = None,
191
+ user_id: Optional[str] = None,
192
+ metadata: Optional[Dict] = None,
193
+ tags: Optional[List[str]] = None,
194
+ ):
195
+ if self.show_logger:
196
+ logger.info(f"SQLAlchemy: update_thread, thread_id={thread_id}")
197
+ if context.session.user is not None:
198
+ user_identifier = context.session.user.identifier
199
+ else:
200
+ raise ValueError("User not found in session context")
201
+ data = {
202
+ "id": thread_id,
203
+ "createdAt": (
204
+ await self.get_current_timestamp() if metadata is None else None
205
+ ),
206
+ "name": (
207
+ name
208
+ if name is not None
209
+ else (metadata.get("name") if metadata and "name" in metadata else None)
210
+ ),
211
+ "userId": user_id,
212
+ "userIdentifier": user_identifier,
213
+ "tags": tags,
214
+ "metadata": json.dumps(metadata) if metadata else None,
215
+ }
216
+ parameters = {
217
+ key: value for key, value in data.items() if value is not None
218
+ } # Remove keys with None values
219
+ columns = ", ".join(f'"{key}"' for key in parameters.keys())
220
+ values = ", ".join(f":{key}" for key in parameters.keys())
221
+ updates = ", ".join(
222
+ f'"{key}" = EXCLUDED."{key}"' for key in parameters.keys() if key != "id"
223
+ )
224
+ query = f"""
225
+ INSERT INTO threads ({columns})
226
+ VALUES ({values})
227
+ ON CONFLICT ("id") DO UPDATE
228
+ SET {updates};
229
+ """
230
+ await self.execute_sql(query=query, parameters=parameters)
231
+
232
+ async def delete_thread(self, thread_id: str):
233
+ if self.show_logger:
234
+ logger.info(f"SQLAlchemy: delete_thread, thread_id={thread_id}")
235
+ # Delete feedbacks/elements/steps/thread
236
+ feedbacks_query = """DELETE FROM feedbacks WHERE "forId" IN (SELECT "id" FROM steps WHERE "threadId" = :id)"""
237
+ elements_query = """DELETE FROM elements WHERE "threadId" = :id"""
238
+ steps_query = """DELETE FROM steps WHERE "threadId" = :id"""
239
+ thread_query = """DELETE FROM threads WHERE "id" = :id"""
240
+ parameters = {"id": thread_id}
241
+ await self.execute_sql(query=feedbacks_query, parameters=parameters)
242
+ await self.execute_sql(query=elements_query, parameters=parameters)
243
+ await self.execute_sql(query=steps_query, parameters=parameters)
244
+ await self.execute_sql(query=thread_query, parameters=parameters)
245
+
246
+ async def list_threads(
247
+ self, pagination: Pagination, filters: ThreadFilter
248
+ ) -> PaginatedResponse:
249
+ if self.show_logger:
250
+ logger.info(
251
+ f"SQLAlchemy: list_threads, pagination={pagination}, filters={filters}"
252
+ )
253
+ if not filters.userId:
254
+ raise ValueError("userId is required")
255
+ all_user_threads: List[ThreadDict] = (
256
+ await self.get_all_user_threads(user_id=filters.userId) or []
257
+ )
258
+
259
+ search_keyword = filters.search.lower() if filters.search else None
260
+ feedback_value = int(filters.feedback) if filters.feedback else None
261
+
262
+ filtered_threads = []
263
+ for thread in all_user_threads:
264
+ keyword_match = True
265
+ feedback_match = True
266
+ if search_keyword or feedback_value is not None:
267
+ if search_keyword:
268
+ keyword_match = any(
269
+ search_keyword in step["output"].lower()
270
+ for step in thread["steps"]
271
+ if "output" in step
272
+ )
273
+ if feedback_value is not None:
274
+ feedback_match = False # Assume no match until found
275
+ for step in thread["steps"]:
276
+ feedback = step.get("feedback")
277
+ if feedback and feedback.get("value") == feedback_value:
278
+ feedback_match = True
279
+ break
280
+ if keyword_match and feedback_match:
281
+ filtered_threads.append(thread)
282
+
283
+ start = 0
284
+ if pagination.cursor:
285
+ for i, thread in enumerate(filtered_threads):
286
+ if (
287
+ thread["id"] == pagination.cursor
288
+ ): # Find the start index using pagination.cursor
289
+ start = i + 1
290
+ break
291
+ end = start + pagination.first
292
+ paginated_threads = filtered_threads[start:end] or []
293
+
294
+ has_next_page = len(filtered_threads) > end
295
+ start_cursor = paginated_threads[0]["id"] if paginated_threads else None
296
+ end_cursor = paginated_threads[-1]["id"] if paginated_threads else None
297
+
298
+ return PaginatedResponse(
299
+ pageInfo=PageInfo(
300
+ hasNextPage=has_next_page,
301
+ startCursor=start_cursor,
302
+ endCursor=end_cursor,
303
+ ),
304
+ data=paginated_threads,
305
+ )
306
+
307
+ ###### Steps ######
308
+ @queue_until_user_message()
309
+ async def create_step(self, step_dict: "StepDict"):
310
+ if self.show_logger:
311
+ logger.info(f"SQLAlchemy: create_step, step_id={step_dict.get('id')}")
312
+ if not getattr(context.session.user, "id", None):
313
+ raise ValueError("No authenticated user in context")
314
+ step_dict["showInput"] = (
315
+ str(step_dict.get("showInput", "")).lower()
316
+ if "showInput" in step_dict
317
+ else None
318
+ )
319
+ parameters = {
320
+ key: value
321
+ for key, value in step_dict.items()
322
+ if value is not None and not (isinstance(value, dict) and not value)
323
+ }
324
+ parameters["metadata"] = json.dumps(step_dict.get("metadata", {}))
325
+ parameters["generation"] = json.dumps(step_dict.get("generation", {}))
326
+ columns = ", ".join(f'"{key}"' for key in parameters.keys())
327
+ values = ", ".join(f":{key}" for key in parameters.keys())
328
+ updates = ", ".join(
329
+ f'"{key}" = :{key}' for key in parameters.keys() if key != "id"
330
+ )
331
+ query = f"""
332
+ INSERT INTO steps ({columns})
333
+ VALUES ({values})
334
+ ON CONFLICT (id) DO UPDATE
335
+ SET {updates};
336
+ """
337
+ await self.execute_sql(query=query, parameters=parameters)
338
+
339
+ @queue_until_user_message()
340
+ async def update_step(self, step_dict: "StepDict"):
341
+ if self.show_logger:
342
+ logger.info(f"SQLAlchemy: update_step, step_id={step_dict.get('id')}")
343
+ await self.create_step(step_dict)
344
+
345
+ @queue_until_user_message()
346
+ async def delete_step(self, step_id: str):
347
+ if self.show_logger:
348
+ logger.info(f"SQLAlchemy: delete_step, step_id={step_id}")
349
+ # Delete feedbacks/elements/steps
350
+ feedbacks_query = """DELETE FROM feedbacks WHERE "forId" = :id"""
351
+ elements_query = """DELETE FROM elements WHERE "forId" = :id"""
352
+ steps_query = """DELETE FROM steps WHERE "id" = :id"""
353
+ parameters = {"id": step_id}
354
+ await self.execute_sql(query=feedbacks_query, parameters=parameters)
355
+ await self.execute_sql(query=elements_query, parameters=parameters)
356
+ await self.execute_sql(query=steps_query, parameters=parameters)
357
+
358
+ ###### Feedback ######
359
+ async def upsert_feedback(self, feedback: Feedback) -> str:
360
+ if self.show_logger:
361
+ logger.info(f"SQLAlchemy: upsert_feedback, feedback_id={feedback.id}")
362
+ feedback.id = feedback.id or str(uuid.uuid4())
363
+ feedback_dict = asdict(feedback)
364
+ parameters = {
365
+ key: value for key, value in feedback_dict.items() if value is not None
366
+ }
367
+
368
+ columns = ", ".join(f'"{key}"' for key in parameters.keys())
369
+ values = ", ".join(f":{key}" for key in parameters.keys())
370
+ updates = ", ".join(
371
+ f'"{key}" = :{key}' for key in parameters.keys() if key != "id"
372
+ )
373
+ query = f"""
374
+ INSERT INTO feedbacks ({columns})
375
+ VALUES ({values})
376
+ ON CONFLICT (id) DO UPDATE
377
+ SET {updates};
378
+ """
379
+ await self.execute_sql(query=query, parameters=parameters)
380
+ return feedback.id
381
+
382
+ async def delete_feedback(self, feedback_id: str) -> bool:
383
+ if self.show_logger:
384
+ logger.info(f"SQLAlchemy: delete_feedback, feedback_id={feedback_id}")
385
+ query = """DELETE FROM feedbacks WHERE "id" = :feedback_id"""
386
+ parameters = {"feedback_id": feedback_id}
387
+ await self.execute_sql(query=query, parameters=parameters)
388
+ return True
389
+
390
+ ###### Elements ######
391
+ @queue_until_user_message()
392
+ async def create_element(self, element: "Element"):
393
+ if self.show_logger:
394
+ logger.info(f"SQLAlchemy: create_element, element_id = {element.id}")
395
+ if not getattr(context.session.user, "id", None):
396
+ raise ValueError("No authenticated user in context")
397
+ if not self.storage_provider:
398
+ logger.warn(
399
+ f"SQLAlchemy: create_element error. No blob_storage_client is configured!"
400
+ )
401
+ return
402
+ if not element.for_id:
403
+ return
404
+
405
+ content: Optional[Union[bytes, str]] = None
406
+
407
+ if element.path:
408
+ async with aiofiles.open(element.path, "rb") as f:
409
+ content = await f.read()
410
+ elif element.url:
411
+ async with aiohttp.ClientSession() as session:
412
+ async with session.get(element.url) as response:
413
+ if response.status == 200:
414
+ content = await response.read()
415
+ else:
416
+ content = None
417
+ elif element.content:
418
+ content = element.content
419
+ else:
420
+ raise ValueError("Element url, path or content must be provided")
421
+ if content is None:
422
+ raise ValueError("Content is None, cannot upload file")
423
+
424
+ context_user = context.session.user
425
+
426
+ user_folder = getattr(context_user, "id", "unknown")
427
+ file_object_key = f"{user_folder}/{element.id}" + (
428
+ f"/{element.name}" if element.name else ""
429
+ )
430
+
431
+ if not element.mime:
432
+ element.mime = "application/octet-stream"
433
+
434
+ uploaded_file = await self.storage_provider.upload_file(
435
+ object_key=file_object_key, data=content, mime=element.mime, overwrite=True
436
+ )
437
+ if not uploaded_file:
438
+ raise ValueError(
439
+ "SQLAlchemy Error: create_element, Failed to persist data in storage_provider"
440
+ )
441
+
442
+ element_dict: ElementDict = element.to_dict()
443
+
444
+ element_dict["url"] = uploaded_file.get("url")
445
+ element_dict["objectKey"] = uploaded_file.get("object_key")
446
+ element_dict_cleaned = {k: v for k, v in element_dict.items() if v is not None}
447
+
448
+ columns = ", ".join(f'"{column}"' for column in element_dict_cleaned.keys())
449
+ placeholders = ", ".join(f":{column}" for column in element_dict_cleaned.keys())
450
+ query = f"INSERT INTO elements ({columns}) VALUES ({placeholders})"
451
+ await self.execute_sql(query=query, parameters=element_dict_cleaned)
452
+
453
+ @queue_until_user_message()
454
+ async def delete_element(self, element_id: str, thread_id: Optional[str] = None):
455
+ if self.show_logger:
456
+ logger.info(f"SQLAlchemy: delete_element, element_id={element_id}")
457
+ query = """DELETE FROM elements WHERE "id" = :id"""
458
+ parameters = {"id": element_id}
459
+ await self.execute_sql(query=query, parameters=parameters)
460
+
461
+ async def delete_user_session(self, id: str) -> bool:
462
+ return False # Not sure why documentation wants this
463
+
464
+ async def get_all_user_threads(
465
+ self, user_id: Optional[str] = None, thread_id: Optional[str] = None
466
+ ) -> Optional[List[ThreadDict]]:
467
+ """Fetch all user threads up to self.user_thread_limit, or one thread by id if thread_id is provided."""
468
+ if self.show_logger:
469
+ logger.info(f"SQLAlchemy: get_all_user_threads")
470
+ user_threads_query = """
471
+ SELECT
472
+ "id" AS thread_id,
473
+ "createdAt" AS thread_createdat,
474
+ "name" AS thread_name,
475
+ "userId" AS user_id,
476
+ "userIdentifier" AS user_identifier,
477
+ "tags" AS thread_tags,
478
+ "metadata" AS thread_metadata
479
+ FROM threads
480
+ WHERE "userId" = :user_id OR "id" = :thread_id
481
+ ORDER BY "createdAt" DESC
482
+ LIMIT :limit
483
+ """
484
+ user_threads = await self.execute_sql(
485
+ query=user_threads_query,
486
+ parameters={
487
+ "user_id": user_id,
488
+ "limit": self.user_thread_limit,
489
+ "thread_id": thread_id,
490
+ },
491
+ )
492
+ if not isinstance(user_threads, list):
493
+ return None
494
+ if not user_threads:
495
+ return []
496
+ else:
497
+ thread_ids = (
498
+ "('"
499
+ + "','".join(map(str, [thread["thread_id"] for thread in user_threads]))
500
+ + "')"
501
+ )
502
+
503
+ steps_feedbacks_query = f"""
504
+ SELECT
505
+ s."id" AS step_id,
506
+ s."name" AS step_name,
507
+ s."type" AS step_type,
508
+ s."threadId" AS step_threadid,
509
+ s."parentId" AS step_parentid,
510
+ s."streaming" AS step_streaming,
511
+ s."waitForAnswer" AS step_waitforanswer,
512
+ s."isError" AS step_iserror,
513
+ s."metadata" AS step_metadata,
514
+ s."tags" AS step_tags,
515
+ s."input" AS step_input,
516
+ s."output" AS step_output,
517
+ s."createdAt" AS step_createdat,
518
+ s."start" AS step_start,
519
+ s."end" AS step_end,
520
+ s."generation" AS step_generation,
521
+ s."showInput" AS step_showinput,
522
+ s."language" AS step_language,
523
+ s."indent" AS step_indent,
524
+ f."value" AS feedback_value,
525
+ f."comment" AS feedback_comment
526
+ FROM steps s LEFT JOIN feedbacks f ON s."id" = f."forId"
527
+ WHERE s."threadId" IN {thread_ids}
528
+ ORDER BY s."createdAt" ASC
529
+ """
530
+ steps_feedbacks = await self.execute_sql(
531
+ query=steps_feedbacks_query, parameters={}
532
+ )
533
+
534
+ elements_query = f"""
535
+ SELECT
536
+ e."id" AS element_id,
537
+ e."threadId" as element_threadid,
538
+ e."type" AS element_type,
539
+ e."chainlitKey" AS element_chainlitkey,
540
+ e."url" AS element_url,
541
+ e."objectKey" as element_objectkey,
542
+ e."name" AS element_name,
543
+ e."display" AS element_display,
544
+ e."size" AS element_size,
545
+ e."language" AS element_language,
546
+ e."page" AS element_page,
547
+ e."forId" AS element_forid,
548
+ e."mime" AS element_mime
549
+ FROM elements e
550
+ WHERE e."threadId" IN {thread_ids}
551
+ """
552
+ elements = await self.execute_sql(query=elements_query, parameters={})
553
+
554
+ thread_dicts = {}
555
+ for thread in user_threads:
556
+ thread_id = thread["thread_id"]
557
+ if thread_id is not None:
558
+ thread_dicts[thread_id] = ThreadDict(
559
+ id=thread_id,
560
+ createdAt=thread["thread_createdat"],
561
+ name=thread["thread_name"],
562
+ userId=thread["user_id"],
563
+ userIdentifier=thread["user_identifier"],
564
+ tags=thread["thread_tags"],
565
+ metadata=thread["thread_metadata"],
566
+ steps=[],
567
+ elements=[],
568
+ )
569
+ # Process steps_feedbacks to populate the steps in the corresponding ThreadDict
570
+ if isinstance(steps_feedbacks, list):
571
+ for step_feedback in steps_feedbacks:
572
+ thread_id = step_feedback["step_threadid"]
573
+ if thread_id is not None:
574
+ feedback = None
575
+ if step_feedback["feedback_value"] is not None:
576
+ feedback = FeedbackDict(
577
+ forId=step_feedback["step_id"],
578
+ id=step_feedback.get("feedback_id"),
579
+ value=step_feedback["feedback_value"],
580
+ comment=step_feedback.get("feedback_comment"),
581
+ )
582
+ step_dict = StepDict(
583
+ id=step_feedback["step_id"],
584
+ name=step_feedback["step_name"],
585
+ type=step_feedback["step_type"],
586
+ threadId=thread_id,
587
+ parentId=step_feedback.get("step_parentid"),
588
+ streaming=step_feedback.get("step_streaming", False),
589
+ waitForAnswer=step_feedback.get("step_waitforanswer"),
590
+ isError=step_feedback.get("step_iserror"),
591
+ metadata=(
592
+ step_feedback["step_metadata"]
593
+ if step_feedback.get("step_metadata") is not None
594
+ else {}
595
+ ),
596
+ tags=step_feedback.get("step_tags"),
597
+ input=(
598
+ step_feedback.get("step_input", "")
599
+ if step_feedback["step_showinput"] == "true"
600
+ else None
601
+ ),
602
+ output=step_feedback.get("step_output", ""),
603
+ createdAt=step_feedback.get("step_createdat"),
604
+ start=step_feedback.get("step_start"),
605
+ end=step_feedback.get("step_end"),
606
+ generation=step_feedback.get("step_generation"),
607
+ showInput=step_feedback.get("step_showinput"),
608
+ language=step_feedback.get("step_language"),
609
+ indent=step_feedback.get("step_indent"),
610
+ feedback=feedback,
611
+ )
612
+ # Append the step to the steps list of the corresponding ThreadDict
613
+ thread_dicts[thread_id]["steps"].append(step_dict)
614
+
615
+ if isinstance(elements, list):
616
+ for element in elements:
617
+ thread_id = element["element_threadid"]
618
+ if thread_id is not None:
619
+ element_dict = ElementDict(
620
+ id=element["element_id"],
621
+ threadId=thread_id,
622
+ type=element["element_type"],
623
+ chainlitKey=element.get("element_chainlitkey"),
624
+ url=element.get("element_url"),
625
+ objectKey=element.get("element_objectkey"),
626
+ name=element["element_name"],
627
+ display=element["element_display"],
628
+ size=element.get("element_size"),
629
+ language=element.get("element_language"),
630
+ autoPlay=element.get("element_autoPlay"),
631
+ playerConfig=element.get("element_playerconfig"),
632
+ page=element.get("element_page"),
633
+ forId=element.get("element_forid"),
634
+ mime=element.get("element_mime"),
635
+ )
636
+ thread_dicts[thread_id]["elements"].append(element_dict) # type: ignore
637
+
638
+ return list(thread_dicts.values())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PraisonAI
3
- Version: 0.0.45
3
+ Version: 0.0.47
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
@@ -51,6 +51,11 @@ Description-Content-Type: text/markdown
51
51
 
52
52
  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.
53
53
 
54
+ | | Cookbook | Open in Colab |
55
+ | --- | --- | --- |
56
+ | Basic | PraisonAI | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a> |
57
+ | Include Tools | PraisonAI Tools | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-tools-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a> |
58
+
54
59
  ## TL;DR
55
60
  ```bash
56
61
  pip install praisonai
@@ -59,6 +64,22 @@ praisonai --init create a movie script about dog in moon
59
64
  praisonai
60
65
  ```
61
66
 
67
+ ## Table of Contents
68
+
69
+ - [Installation](#installation)
70
+ - [Initialise](#initialise)
71
+ - [Run](#run)
72
+ - [Full Automatic Mode](#full-automatic-mode)
73
+ - [User Interface](#user-interface)
74
+ - [Praison AI Chat](#praison-ai-chat)
75
+ - [Create Custom Tools](#create-custom-tools)
76
+ - [Agents Playbook](#agents-playbook)
77
+ - [Include praisonai package in your project](#include-praisonai-package-in-your-project)
78
+ - [Commands to Install Dev Dependencies](#commands-to-install-dependencies)
79
+ - [Other Models](#other-models)
80
+ - [Contributing](#contributing)
81
+ - [Star History](#star-history)
82
+
62
83
  ## Installation
63
84
 
64
85
  ```bash
@@ -126,29 +147,26 @@ or
126
147
  python -m praisonai ui
127
148
  ```
128
149
 
129
- ## Create Custom Tools
150
+ ## Praison AI Chat
130
151
 
131
- ### TL;DR to Create a Custom Tool
152
+ * https://docs.praison.ai/chat/
132
153
 
133
154
  ```bash
134
- pip install praisonai duckduckgo-search
155
+ pip install "praisonai[chat]"
135
156
  export OPENAI_API_KEY="Enter your API key"
136
- praisonai --init research about the latest AI News and prepare a detailed report
157
+ praisonai chat
137
158
  ```
138
159
 
139
- - Add `- InternetSearchTool` in the agents.yaml file in the tools section.
140
- - Create a file called tools.py and add this code [tools.py](./tools.py)
160
+ ## Create Custom Tools
141
161
 
142
- ```bash
143
- praisonai
144
- ```
162
+ * https://docs.praison.ai/tools/custom/
145
163
 
146
- ### Pre-requisite to Create a Custom Tool
164
+ ### Step 1: Pre-requisite to Create a Custom Tool
147
165
  `agents.yaml` file should be present in the current directory.
148
166
 
149
167
  If it doesn't exist, create it by running the command `praisonai --init research about the latest AI News and prepare a detailed report`.
150
168
 
151
- ### Step 1 to Create a Custom Tool
169
+ ### Step 2: to Create a Custom Tool
152
170
 
153
171
  Create a file called tools.py in the same directory as the agents.yaml file.
154
172
 
@@ -167,7 +185,7 @@ class InternetSearchTool(BaseTool):
167
185
  return results
168
186
  ```
169
187
 
170
- ### Step 2 to Create a Custom Tool
188
+ ### Step 3: to Create a Custom Tool
171
189
 
172
190
  Add the tool to the agents.yaml file as show below under the tools section `- InternetSearchTool`.
173
191
 
@@ -206,37 +224,63 @@ roles:
206
224
  expected_output: 'Complete script ready for production.'
207
225
  ```
208
226
 
227
+ ## Use 100+ Models
228
+
229
+ * https://docs.praison.ai/models/
230
+
209
231
  ## Include praisonai package in your project
210
232
 
233
+ * https://docs.praison.ai/developers/wrapper
234
+ * https://docs.praison.ai/developers/wrapper-tools/
235
+
236
+ ## Option 1: Using RAW YAML
237
+
238
+ ```python
239
+ from praisonai import PraisonAI
240
+
241
+ # Example agent_yaml content
242
+ agent_yaml = """
243
+ framework: "crewai"
244
+ topic: "Space Exploration"
245
+
246
+ roles:
247
+ astronomer:
248
+ role: "Space Researcher"
249
+ goal: "Discover new insights about {topic}"
250
+ backstory: "You are a curious and dedicated astronomer with a passion for unraveling the mysteries of the cosmos."
251
+ tasks:
252
+ investigate_exoplanets:
253
+ description: "Research and compile information about exoplanets discovered in the last decade."
254
+ expected_output: "A summarized report on exoplanet discoveries, including their size, potential habitability, and distance from Earth."
255
+ """
256
+
257
+ # Create a PraisonAI instance with the agent_yaml content
258
+ praisonai = PraisonAI(agent_yaml=agent_yaml)
259
+
260
+ # Run PraisonAI
261
+ result = praisonai.run()
262
+
263
+ # Print the result
264
+ print(result)
265
+ ```
266
+
267
+ ## Option 2: Using separate agents.yaml file
268
+
269
+
270
+ Note: Please create agents.yaml file before hand.
271
+
211
272
  ```python
212
273
  from praisonai import PraisonAI
213
274
 
214
275
  def basic(): # Basic Mode
215
- praison_ai = PraisonAI(agent_file="agents.yaml")
216
- praison_ai.main()
217
-
218
- def advanced(): # Advanced Mode with options
219
- praison_ai = PraisonAI(
220
- agent_file="agents.yaml",
221
- framework="autogen",
222
- )
223
- praison_ai.main()
224
-
225
- def auto(): # Full Automatic Mode
226
- praison_ai = PraisonAI(
227
- auto="Create a movie script about car in mars",
228
- framework="autogen"
229
- )
230
- print(praison_ai.framework)
231
- praison_ai.main()
276
+ praisonai = PraisonAI(agent_file="agents.yaml")
277
+ praisonai.run()
232
278
 
233
279
  if __name__ == "__main__":
234
280
  basic()
235
- advanced()
236
- auto()
237
281
  ```
238
282
 
239
- ### Commands to Install Dependencies:
283
+ ## Commands to Install Dependencies:
240
284
 
241
285
  1. **Install all dependencies, including dev dependencies:**
242
286
  ```sh
@@ -260,30 +304,6 @@ if __name__ == "__main__":
260
304
 
261
305
  This configuration ensures that your development dependencies are correctly categorized and installed as needed.
262
306
 
263
- ## Other Models
264
-
265
- ```bash
266
- # Ollama
267
- OPENAI_API_BASE='http://localhost:11434/v1'
268
- OPENAI_MODEL_NAME='mistral'
269
- OPENAI_API_KEY='NA'
270
-
271
- # FastChat
272
- OPENAI_API_BASE="http://localhost:8001/v1"
273
- OPENAI_MODEL_NAME='oh-2.5m7b-q51'
274
- OPENAI_API_KEY=NA
275
-
276
- # LM Studio
277
- OPENAI_API_BASE="http://localhost:1234/v1"
278
- OPENAI_MODEL_NAME=NA
279
- OPENAI_API_KEY=NA
280
-
281
- # Mistral API
282
- OPENAI_API_BASE=https://api.mistral.ai/v1
283
- OPENAI_MODEL_NAME="mistral-small"
284
- OPENAI_API_KEY=your-mistral-api-key
285
- ```
286
-
287
307
  ## Contributing
288
308
 
289
309
  - Fork on GitHub: Use the "Fork" button on the repository page.
@@ -3,8 +3,8 @@ praisonai/__main__.py,sha256=MVgsjMThjBexHt4nhd760JCqvP4x0IQcwo8kULOK4FQ,144
3
3
  praisonai/agents_generator.py,sha256=8d1WRbubvEkBrW1HZ7_xnGyqgJi0yxmXa3MgTIqef1c,19127
4
4
  praisonai/auto.py,sha256=9spTXqj47Hmmqv5QHRYE_RzSVHH_KoPbaZjskUj2UcE,7895
5
5
  praisonai/chainlit_ui.py,sha256=bNR7s509lp0I9JlJNvwCZRUZosC64qdvlFCt8NmFamQ,12216
6
- praisonai/cli.py,sha256=RAPXxz6FxEG8Ek7XVRrzZ0FE1fwNHRd-prOjVjJjd0k,12180
7
- praisonai/deploy.py,sha256=ppKuPfiAyt88-atB8TnPZiNfdfXmjuyqTemCyz4r7lE,6031
6
+ praisonai/cli.py,sha256=JBI3JjI7RB0QQipuy-utJpP3jxj8gnG5roMLW2Zjq6A,12851
7
+ praisonai/deploy.py,sha256=4lahg-H78WZXKbJynxA1IvSOUPyV8USS5QFvFGTPGro,6028
8
8
  praisonai/inbuilt_tools/__init__.py,sha256=mUKnbL6Gram9c9f2m8wJwEzURBLmPEOcHzwySBH89YA,74
9
9
  praisonai/inbuilt_tools/autogen_tools.py,sha256=svYkM2N7DVFvbiwgoAS7U_MqTOD8rHf8VD3BaFUV5_Y,14907
10
10
  praisonai/inc/__init__.py,sha256=sPDlYBBwdk0VlWzaaM_lG0_LD07lS2HRGvPdxXJFiYg,62
@@ -22,16 +22,17 @@ praisonai/public/logo_light.png,sha256=8cQRti_Ysa30O3_7C3ku2w40LnVUUlUok47H-3ZZH
22
22
  praisonai/public/movie.svg,sha256=aJ2EQ8vXZusVsF2SeuAVxP4RFJzQ14T26ejrGYdBgzk,1289
23
23
  praisonai/public/thriller.svg,sha256=2dYY72EcgbEyTxS4QzjAm37Y4srtPWEW4vCMFki98ZI,3163
24
24
  praisonai/test.py,sha256=RZKq3UEFb6AnFFiHER3zBXfNmlteSLBlrTmOvnpnZLo,4092
25
- praisonai/ui/chat.py,sha256=r_YWY-gZZSJEYM8wDE2B1owZgF2VTCD42oqLbqXntqg,14611
25
+ praisonai/ui/chat.py,sha256=S3a5u0mI7RO5QFbKckz4z8b32gRTiX8kauSHvQBTMco,9238
26
26
  praisonai/ui/public/fantasy.svg,sha256=4Gs3kIOux-pjGtw6ogI_rv5_viVJxnE5gRwGilsSg0o,1553
27
27
  praisonai/ui/public/game.svg,sha256=y2QMaA01m8XzuDjTOBWzupOC3-TpnUl9ah89mIhviUw,2406
28
28
  praisonai/ui/public/logo_dark.png,sha256=frHz1zkrnivGssJgk9iy1cabojkVgm8B4MllFwL_CnI,17050
29
29
  praisonai/ui/public/logo_light.png,sha256=8cQRti_Ysa30O3_7C3ku2w40LnVUUlUok47H-3ZZHSU,19656
30
30
  praisonai/ui/public/movie.svg,sha256=aJ2EQ8vXZusVsF2SeuAVxP4RFJzQ14T26ejrGYdBgzk,1289
31
31
  praisonai/ui/public/thriller.svg,sha256=2dYY72EcgbEyTxS4QzjAm37Y4srtPWEW4vCMFki98ZI,3163
32
+ praisonai/ui/sql_alchemy.py,sha256=HsyeRq-G9qbQobHWpTJHHKQiT4FvYw_7iuv-2PNh0IU,27419
32
33
  praisonai/version.py,sha256=ugyuFliEqtAwQmH4sTlc16YXKYbFWDmfyk87fErB8-8,21
33
- praisonai-0.0.45.dist-info/LICENSE,sha256=kqvFysVlnFxYOu0HxCe2HlmZmJtdmNGOxWRRkT9TsWc,1035
34
- praisonai-0.0.45.dist-info/METADATA,sha256=sAnmn5xh_PuYCpdhJMhV4LC7S4QKg2o4xN72SDyLMtQ,8088
35
- praisonai-0.0.45.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
36
- praisonai-0.0.45.dist-info/entry_points.txt,sha256=Qg41eW3A1-dvdV5tF7LqChfYof8Rihk2rN1fiEE3vnk,53
37
- praisonai-0.0.45.dist-info/RECORD,,
34
+ praisonai-0.0.47.dist-info/LICENSE,sha256=kqvFysVlnFxYOu0HxCe2HlmZmJtdmNGOxWRRkT9TsWc,1035
35
+ praisonai-0.0.47.dist-info/METADATA,sha256=FH2jJ9hDTdpt4pNpl4hR8Pige1GuzlISpLBs45F-FK0,9301
36
+ praisonai-0.0.47.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
37
+ praisonai-0.0.47.dist-info/entry_points.txt,sha256=Qg41eW3A1-dvdV5tF7LqChfYof8Rihk2rN1fiEE3vnk,53
38
+ praisonai-0.0.47.dist-info/RECORD,,