PraisonAI 2.0.1__cp311-cp311-macosx_15_0_arm64.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.

Files changed (81) hide show
  1. praisonai/__init__.py +6 -0
  2. praisonai/__main__.py +10 -0
  3. praisonai/agents_generator.py +615 -0
  4. praisonai/api/call.py +292 -0
  5. praisonai/auto.py +238 -0
  6. praisonai/chainlit_ui.py +304 -0
  7. praisonai/cli.py +512 -0
  8. praisonai/deploy.py +138 -0
  9. praisonai/inbuilt_tools/__init__.py +24 -0
  10. praisonai/inbuilt_tools/autogen_tools.py +117 -0
  11. praisonai/inc/__init__.py +2 -0
  12. praisonai/inc/config.py +96 -0
  13. praisonai/inc/models.py +128 -0
  14. praisonai/public/android-chrome-192x192.png +0 -0
  15. praisonai/public/android-chrome-512x512.png +0 -0
  16. praisonai/public/apple-touch-icon.png +0 -0
  17. praisonai/public/fantasy.svg +3 -0
  18. praisonai/public/favicon-16x16.png +0 -0
  19. praisonai/public/favicon-32x32.png +0 -0
  20. praisonai/public/favicon.ico +0 -0
  21. praisonai/public/game.svg +3 -0
  22. praisonai/public/logo_dark.png +0 -0
  23. praisonai/public/logo_light.png +0 -0
  24. praisonai/public/movie.svg +3 -0
  25. praisonai/public/thriller.svg +3 -0
  26. praisonai/setup/__init__.py +1 -0
  27. praisonai/setup/build.py +21 -0
  28. praisonai/setup/config.yaml +60 -0
  29. praisonai/setup/post_install.py +23 -0
  30. praisonai/setup/setup_conda_env.py +25 -0
  31. praisonai/setup/setup_conda_env.sh +72 -0
  32. praisonai/setup.py +16 -0
  33. praisonai/test.py +105 -0
  34. praisonai/train.py +276 -0
  35. praisonai/ui/README.md +21 -0
  36. praisonai/ui/chat.py +387 -0
  37. praisonai/ui/code.py +440 -0
  38. praisonai/ui/components/aicoder.py +269 -0
  39. praisonai/ui/config/.chainlit/config.toml +120 -0
  40. praisonai/ui/config/.chainlit/translations/bn.json +231 -0
  41. praisonai/ui/config/.chainlit/translations/en-US.json +229 -0
  42. praisonai/ui/config/.chainlit/translations/gu.json +231 -0
  43. praisonai/ui/config/.chainlit/translations/he-IL.json +231 -0
  44. praisonai/ui/config/.chainlit/translations/hi.json +231 -0
  45. praisonai/ui/config/.chainlit/translations/kn.json +231 -0
  46. praisonai/ui/config/.chainlit/translations/ml.json +231 -0
  47. praisonai/ui/config/.chainlit/translations/mr.json +231 -0
  48. praisonai/ui/config/.chainlit/translations/ta.json +231 -0
  49. praisonai/ui/config/.chainlit/translations/te.json +231 -0
  50. praisonai/ui/config/.chainlit/translations/zh-CN.json +229 -0
  51. praisonai/ui/config/chainlit.md +1 -0
  52. praisonai/ui/config/translations/bn.json +231 -0
  53. praisonai/ui/config/translations/en-US.json +229 -0
  54. praisonai/ui/config/translations/gu.json +231 -0
  55. praisonai/ui/config/translations/he-IL.json +231 -0
  56. praisonai/ui/config/translations/hi.json +231 -0
  57. praisonai/ui/config/translations/kn.json +231 -0
  58. praisonai/ui/config/translations/ml.json +231 -0
  59. praisonai/ui/config/translations/mr.json +231 -0
  60. praisonai/ui/config/translations/ta.json +231 -0
  61. praisonai/ui/config/translations/te.json +231 -0
  62. praisonai/ui/config/translations/zh-CN.json +229 -0
  63. praisonai/ui/context.py +283 -0
  64. praisonai/ui/db.py +291 -0
  65. praisonai/ui/public/fantasy.svg +3 -0
  66. praisonai/ui/public/game.svg +3 -0
  67. praisonai/ui/public/logo_dark.png +0 -0
  68. praisonai/ui/public/logo_light.png +0 -0
  69. praisonai/ui/public/movie.svg +3 -0
  70. praisonai/ui/public/praison.css +3 -0
  71. praisonai/ui/public/thriller.svg +3 -0
  72. praisonai/ui/realtime.py +421 -0
  73. praisonai/ui/realtimeclient/__init__.py +653 -0
  74. praisonai/ui/realtimeclient/realtimedocs.txt +1484 -0
  75. praisonai/ui/realtimeclient/tools.py +236 -0
  76. praisonai/ui/sql_alchemy.py +706 -0
  77. praisonai/version.py +1 -0
  78. praisonai-2.0.1.dist-info/LICENSE +20 -0
  79. praisonai-2.0.1.dist-info/METADATA +483 -0
  80. praisonai-2.0.1.dist-info/RECORD +81 -0
  81. praisonai-2.0.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,421 @@
1
+ import os
2
+ import asyncio
3
+ import sqlite3
4
+ from datetime import datetime
5
+ from uuid import uuid4
6
+
7
+ from openai import AsyncOpenAI
8
+ import chainlit as cl
9
+ from chainlit.input_widget import TextInput
10
+ from chainlit.types import ThreadDict
11
+
12
+ from realtimeclient import RealtimeClient
13
+ from realtimeclient.tools import tools
14
+ from sql_alchemy import SQLAlchemyDataLayer
15
+ import chainlit.data as cl_data
16
+ from literalai.helper import utc_now
17
+ import json
18
+ import logging
19
+ import importlib.util
20
+ from importlib import import_module
21
+ from pathlib import Path
22
+
23
+ # Set up logging
24
+ logger = logging.getLogger(__name__)
25
+ log_level = os.getenv("LOGLEVEL", "INFO").upper()
26
+ logger.handlers = []
27
+
28
+ # Set up logging to console
29
+ console_handler = logging.StreamHandler()
30
+ console_handler.setLevel(log_level)
31
+ console_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
32
+ console_handler.setFormatter(console_formatter)
33
+ logger.addHandler(console_handler)
34
+
35
+ # Set the logging level for the logger
36
+ logger.setLevel(log_level)
37
+
38
+ # Set up CHAINLIT_AUTH_SECRET
39
+ CHAINLIT_AUTH_SECRET = os.getenv("CHAINLIT_AUTH_SECRET")
40
+
41
+ if not CHAINLIT_AUTH_SECRET:
42
+ os.environ["CHAINLIT_AUTH_SECRET"] = "p8BPhQChpg@J>jBz$wGxqLX2V>yTVgP*7Ky9H$aV:axW~ANNX-7_T:o@lnyCBu^U"
43
+ CHAINLIT_AUTH_SECRET = os.getenv("CHAINLIT_AUTH_SECRET")
44
+
45
+ # Database path
46
+ DB_PATH = os.path.expanduser("~/.praison/database.sqlite")
47
+
48
+ def initialize_db():
49
+ os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
50
+ conn = sqlite3.connect(DB_PATH)
51
+ cursor = conn.cursor()
52
+ cursor.execute('''
53
+ CREATE TABLE IF NOT EXISTS users (
54
+ id UUID PRIMARY KEY,
55
+ identifier TEXT NOT NULL UNIQUE,
56
+ metadata JSONB NOT NULL,
57
+ createdAt TEXT
58
+ )
59
+ ''')
60
+ cursor.execute('''
61
+ CREATE TABLE IF NOT EXISTS threads (
62
+ id UUID PRIMARY KEY,
63
+ createdAt TEXT,
64
+ name TEXT,
65
+ userId UUID,
66
+ userIdentifier TEXT,
67
+ tags TEXT[],
68
+ metadata JSONB NOT NULL DEFAULT '{}',
69
+ FOREIGN KEY (userId) REFERENCES users(id) ON DELETE CASCADE
70
+ )
71
+ ''')
72
+ cursor.execute('''
73
+ CREATE TABLE IF NOT EXISTS steps (
74
+ id UUID PRIMARY KEY,
75
+ name TEXT NOT NULL,
76
+ type TEXT NOT NULL,
77
+ threadId UUID NOT NULL,
78
+ parentId UUID,
79
+ disableFeedback BOOLEAN NOT NULL DEFAULT 0,
80
+ streaming BOOLEAN NOT NULL DEFAULT 0,
81
+ waitForAnswer BOOLEAN DEFAULT 0,
82
+ isError BOOLEAN NOT NULL DEFAULT 0,
83
+ metadata JSONB DEFAULT '{}',
84
+ tags TEXT[],
85
+ input TEXT,
86
+ output TEXT,
87
+ createdAt TEXT,
88
+ start TEXT,
89
+ end TEXT,
90
+ generation JSONB,
91
+ showInput TEXT,
92
+ language TEXT,
93
+ indent INT,
94
+ FOREIGN KEY (threadId) REFERENCES threads (id) ON DELETE CASCADE
95
+ )
96
+ ''')
97
+ cursor.execute('''
98
+ CREATE TABLE IF NOT EXISTS elements (
99
+ id UUID PRIMARY KEY,
100
+ threadId UUID,
101
+ type TEXT,
102
+ url TEXT,
103
+ chainlitKey TEXT,
104
+ name TEXT NOT NULL,
105
+ display TEXT,
106
+ objectKey TEXT,
107
+ size TEXT,
108
+ page INT,
109
+ language TEXT,
110
+ forId UUID,
111
+ mime TEXT,
112
+ FOREIGN KEY (threadId) REFERENCES threads (id) ON DELETE CASCADE
113
+ )
114
+ ''')
115
+ cursor.execute('''
116
+ CREATE TABLE IF NOT EXISTS feedbacks (
117
+ id UUID PRIMARY KEY,
118
+ forId UUID NOT NULL,
119
+ value INT NOT NULL,
120
+ threadId UUID,
121
+ comment TEXT
122
+ )
123
+ ''')
124
+ cursor.execute('''
125
+ CREATE TABLE IF NOT EXISTS settings (
126
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
127
+ key TEXT UNIQUE,
128
+ value TEXT
129
+ )
130
+ ''')
131
+ conn.commit()
132
+ conn.close()
133
+
134
+ def save_setting(key: str, value: str):
135
+ """Saves a setting to the database."""
136
+ conn = sqlite3.connect(DB_PATH)
137
+ cursor = conn.cursor()
138
+ cursor.execute(
139
+ """
140
+ INSERT OR REPLACE INTO settings (id, key, value)
141
+ VALUES ((SELECT id FROM settings WHERE key = ?), ?, ?)
142
+ """,
143
+ (key, key, value),
144
+ )
145
+ conn.commit()
146
+ conn.close()
147
+
148
+ def load_setting(key: str) -> str:
149
+ """Loads a setting from the database."""
150
+ conn = sqlite3.connect(DB_PATH)
151
+ cursor = conn.cursor()
152
+ cursor.execute('SELECT value FROM settings WHERE key = ?', (key,))
153
+ result = cursor.fetchone()
154
+ conn.close()
155
+ return result[0] if result else None
156
+
157
+ # Initialize the database
158
+ initialize_db()
159
+
160
+ # Set up SQLAlchemy data layer
161
+ cl_data._data_layer = SQLAlchemyDataLayer(conninfo=f"sqlite+aiosqlite:///{DB_PATH}")
162
+
163
+ client = AsyncOpenAI()
164
+
165
+ # Try to import tools from the root directory
166
+ tools_path = os.path.join(os.getcwd(), 'tools.py')
167
+ logger.info(f"Tools path: {tools_path}")
168
+
169
+ def import_tools_from_file(file_path):
170
+ spec = importlib.util.spec_from_file_location("custom_tools", file_path)
171
+ custom_tools_module = importlib.util.module_from_spec(spec)
172
+ spec.loader.exec_module(custom_tools_module)
173
+ logger.debug(f"Imported tools from {file_path}")
174
+ logger.debug(f"Tools: {custom_tools_module}")
175
+ return custom_tools_module
176
+
177
+ try:
178
+ if os.path.exists(tools_path):
179
+ # tools.py exists in the root directory, import from file
180
+ custom_tools_module = import_tools_from_file(tools_path)
181
+ logger.info("Successfully imported custom tools from root tools.py")
182
+ else:
183
+ logger.info("No custom tools.py file found in the root directory")
184
+ custom_tools_module = None
185
+
186
+ if custom_tools_module:
187
+ # Update the tools list with custom tools
188
+ if hasattr(custom_tools_module, 'tools') and isinstance(custom_tools_module.tools, list):
189
+ tools.extend(custom_tools_module.tools)
190
+ else:
191
+ for name, obj in custom_tools_module.__dict__.items():
192
+ if callable(obj) and not name.startswith("__"):
193
+ tools.append(({"type": "function", "function": obj}, obj))
194
+
195
+ except Exception as e:
196
+ logger.warning(f"Error importing custom tools: {str(e)}. Continuing without custom tools.")
197
+
198
+ @cl.on_chat_start
199
+ async def start():
200
+ initialize_db()
201
+ model_name = load_setting("model_name") or os.getenv("MODEL_NAME", "gpt-4o-mini")
202
+ cl.user_session.set("model_name", model_name)
203
+ cl.user_session.set("message_history", []) # Initialize message history
204
+ logger.debug(f"Model name: {model_name}")
205
+ # settings = cl.ChatSettings(
206
+ # [
207
+ # TextInput(
208
+ # id="model_name",
209
+ # label="Enter the Model Name",
210
+ # placeholder="e.g., gpt-4o-mini",
211
+ # initial=model_name
212
+ # )
213
+ # ]
214
+ # )
215
+ # cl.user_session.set("settings", settings)
216
+ # await settings.send()
217
+ await cl.Message(
218
+ content="Welcome to the PraisonAI realtime. Press `P` to talk!"
219
+ ).send()
220
+ await setup_openai_realtime()
221
+
222
+ @cl.on_message
223
+ async def on_message(message: cl.Message):
224
+ openai_realtime: RealtimeClient = cl.user_session.get("openai_realtime")
225
+ message_history = cl.user_session.get("message_history", [])
226
+
227
+ if openai_realtime and openai_realtime.is_connected():
228
+ current_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
229
+ prompt = f"Current time Just for reference: {current_date}\n\n{message.content}"
230
+
231
+ # Add user message to history
232
+ message_history.append({"role": "user", "content": prompt})
233
+ cl.user_session.set("message_history", message_history)
234
+
235
+ await openai_realtime.send_user_message_content([{ "type": 'input_text', "text": message.content }])
236
+ else:
237
+ await cl.Message(content="Please activate voice mode before sending messages!").send()
238
+
239
+ async def setup_openai_realtime():
240
+ """Instantiate and configure the OpenAI Realtime Client"""
241
+ openai_realtime = RealtimeClient(api_key=os.getenv("OPENAI_API_KEY"))
242
+ cl.user_session.set("track_id", str(uuid4()))
243
+
244
+ async def handle_conversation_updated(event):
245
+ item = event.get("item")
246
+ delta = event.get("delta")
247
+ """Currently used to stream audio back to the client."""
248
+ if delta:
249
+ if 'audio' in delta:
250
+ audio = delta['audio'] # Int16Array, audio added
251
+ await cl.context.emitter.send_audio_chunk(cl.OutputAudioChunk(mimeType="pcm16", data=audio, track=cl.user_session.get("track_id")))
252
+ if 'transcript' in delta:
253
+ transcript = delta['transcript'] # string, transcript added
254
+ logger.debug(f"Transcript delta: {transcript}")
255
+ if 'text' in delta:
256
+ text = delta['text'] # string, text added
257
+ logger.debug(f"Text delta: {text}")
258
+ if 'arguments' in delta:
259
+ arguments = delta['arguments'] # string, function arguments added
260
+ logger.debug(f"Function arguments delta: {arguments}")
261
+
262
+ async def handle_item_completed(event):
263
+ """Used to populate the chat context with transcription once an item is completed."""
264
+ try:
265
+ item = event.get("item")
266
+ logger.debug(f"Item completed: {json.dumps(item, indent=2, default=str)}")
267
+ await openai_realtime._send_chainlit_message(item)
268
+
269
+ # Add assistant message to history
270
+ message_history = cl.user_session.get("message_history", [])
271
+ content = item.get("formatted", {}).get("text", "") or item.get("formatted", {}).get("transcript", "")
272
+ if content:
273
+ message_history.append({"role": "assistant", "content": content})
274
+ cl.user_session.set("message_history", message_history)
275
+ except Exception as e:
276
+ error_message = f"Error in handle_item_completed: {str(e)}"
277
+ logger.error(error_message)
278
+ debug_item = json.dumps(item, indent=2, default=str)
279
+ logger.error(f"Item causing error: {debug_item}")
280
+
281
+ async def handle_conversation_interrupt(event):
282
+ """Used to cancel the client previous audio playback."""
283
+ cl.user_session.set("track_id", str(uuid4()))
284
+ await cl.context.emitter.send_audio_interrupt()
285
+
286
+ async def handle_error(event):
287
+ logger.error(event)
288
+ await cl.Message(content=f"Error: {event}", author="System").send()
289
+
290
+ openai_realtime.on('conversation.updated', handle_conversation_updated)
291
+ openai_realtime.on('conversation.item.completed', handle_item_completed)
292
+ openai_realtime.on('conversation.interrupted', handle_conversation_interrupt)
293
+ openai_realtime.on('error', handle_error)
294
+
295
+ cl.user_session.set("openai_realtime", openai_realtime)
296
+ coros = [openai_realtime.add_tool(tool_def, tool_handler) for tool_def, tool_handler in tools]
297
+ await asyncio.gather(*coros)
298
+
299
+ @cl.on_settings_update
300
+ async def setup_agent(settings):
301
+ logger.debug(settings)
302
+ cl.user_session.set("settings", settings)
303
+ model_name = settings["model_name"]
304
+ cl.user_session.set("model_name", model_name)
305
+
306
+ # Save in settings table
307
+ save_setting("model_name", model_name)
308
+
309
+ # Save in thread metadata
310
+ thread_id = cl.user_session.get("thread_id")
311
+ if thread_id:
312
+ thread = await cl_data._data_layer.get_thread(thread_id)
313
+ if thread:
314
+ metadata = thread.get("metadata", {})
315
+ if isinstance(metadata, str):
316
+ try:
317
+ metadata = json.loads(metadata)
318
+ except json.JSONDecodeError:
319
+ metadata = {}
320
+
321
+ metadata["model_name"] = model_name
322
+
323
+ # Always store metadata as a dictionary
324
+ await cl_data._data_layer.update_thread(thread_id, metadata=metadata)
325
+
326
+ # Update the user session with the new metadata
327
+ cl.user_session.set("metadata", metadata)
328
+
329
+ @cl.on_audio_start
330
+ async def on_audio_start():
331
+ try:
332
+ openai_realtime: RealtimeClient = cl.user_session.get("openai_realtime")
333
+ await openai_realtime.connect()
334
+ logger.info("Connected to OpenAI realtime")
335
+ return True
336
+ except Exception as e:
337
+ await cl.ErrorMessage(content=f"Failed to connect to OpenAI realtime: {e}").send()
338
+ return False
339
+
340
+ @cl.on_audio_chunk
341
+ async def on_audio_chunk(chunk: cl.InputAudioChunk):
342
+ openai_realtime: RealtimeClient = cl.user_session.get("openai_realtime")
343
+ if openai_realtime.is_connected():
344
+ await openai_realtime.append_input_audio(chunk.data)
345
+ else:
346
+ logger.info("RealtimeClient is not connected")
347
+
348
+ @cl.on_audio_end
349
+ @cl.on_chat_end
350
+ @cl.on_stop
351
+ async def on_end():
352
+ openai_realtime: RealtimeClient = cl.user_session.get("openai_realtime")
353
+ if openai_realtime and openai_realtime.is_connected():
354
+ await openai_realtime.disconnect()
355
+
356
+ @cl.password_auth_callback
357
+ def auth_callback(username: str, password: str):
358
+ # You can customize this function to use your own authentication logic
359
+ expected_username = os.getenv("CHAINLIT_USERNAME", "admin")
360
+ expected_password = os.getenv("CHAINLIT_PASSWORD", "admin")
361
+ if (username, password) == (expected_username, expected_password):
362
+ return cl.User(
363
+ identifier=username, metadata={"role": "ADMIN", "provider": "credentials"}
364
+ )
365
+ else:
366
+ return None
367
+
368
+ @cl.on_chat_resume
369
+ async def on_chat_resume(thread: ThreadDict):
370
+ logger.info(f"Resuming chat: {thread['id']}")
371
+ model_name = load_setting("model_name") or os.getenv("MODEL_NAME") or "gpt-4o-mini"
372
+ logger.debug(f"Model name: {model_name}")
373
+ settings = cl.ChatSettings(
374
+ [
375
+ TextInput(
376
+ id="model_name",
377
+ label="Enter the Model Name",
378
+ placeholder="e.g., gpt-4o-mini",
379
+ initial=model_name
380
+ )
381
+ ]
382
+ )
383
+ await settings.send()
384
+ thread_id = thread["id"]
385
+ cl.user_session.set("thread_id", thread["id"])
386
+
387
+ # Ensure metadata is a dictionary
388
+ metadata = thread.get("metadata", {})
389
+ if isinstance(metadata, str):
390
+ try:
391
+ metadata = json.loads(metadata)
392
+ except json.JSONDecodeError:
393
+ metadata = {}
394
+
395
+ cl.user_session.set("metadata", metadata)
396
+
397
+ message_history = []
398
+ steps = thread["steps"]
399
+
400
+ for message in steps:
401
+ msg_type = message.get("type")
402
+ if msg_type == "user_message":
403
+ message_history.append({"role": "user", "content": message.get("output", "")})
404
+ elif msg_type == "assistant_message":
405
+ message_history.append({"role": "assistant", "content": message.get("output", "")})
406
+ elif msg_type == "run":
407
+ # Handle 'run' type messages
408
+ if message.get("isError"):
409
+ message_history.append({"role": "system", "content": f"Error: {message.get('output', '')}"})
410
+ else:
411
+ # You might want to handle non-error 'run' messages differently
412
+ pass
413
+ else:
414
+ logger.warning(f"Message without recognized type: {message}")
415
+
416
+ cl.user_session.set("message_history", message_history)
417
+
418
+ # Reconnect to OpenAI realtime
419
+ await setup_openai_realtime()
420
+
421
+