praisonaiagents 0.0.27__py3-none-any.whl → 0.0.29__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.
- praisonaiagents/__init__.py +4 -0
- praisonaiagents/agent/agent.py +7 -3
- praisonaiagents/agents/__init__.py +2 -1
- praisonaiagents/agents/agents.py +169 -35
- praisonaiagents/agents/autoagents.py +335 -0
- praisonaiagents/memory/memory.py +928 -0
- praisonaiagents/task/task.py +157 -9
- {praisonaiagents-0.0.27.dist-info → praisonaiagents-0.0.29.dist-info}/METADATA +4 -2
- {praisonaiagents-0.0.27.dist-info → praisonaiagents-0.0.29.dist-info}/RECORD +11 -17
- {praisonaiagents-0.0.27.dist-info → praisonaiagents-0.0.29.dist-info}/WHEEL +1 -1
- praisonaiagents/build/lib/praisonaiagents/__init__.py +0 -1
- praisonaiagents/build/lib/praisonaiagents/agent/__init__.py +0 -4
- praisonaiagents/build/lib/praisonaiagents/agent/agent.py +0 -350
- praisonaiagents/build/lib/praisonaiagents/agents/__init__.py +0 -4
- praisonaiagents/build/lib/praisonaiagents/agents/agents.py +0 -318
- praisonaiagents/build/lib/praisonaiagents/main.py +0 -112
- praisonaiagents/build/lib/praisonaiagents/task/__init__.py +0 -4
- praisonaiagents/build/lib/praisonaiagents/task/task.py +0 -48
- {praisonaiagents-0.0.27.dist-info → praisonaiagents-0.0.29.dist-info}/top_level.txt +0 -0
praisonaiagents/__init__.py
CHANGED
@@ -6,6 +6,8 @@ from .agent.agent import Agent
|
|
6
6
|
from .agents.agents import PraisonAIAgents
|
7
7
|
from .task.task import Task
|
8
8
|
from .tools.tools import Tools
|
9
|
+
from .agents.autoagents import AutoAgents
|
10
|
+
from .memory.memory import Memory
|
9
11
|
from .main import (
|
10
12
|
TaskOutput,
|
11
13
|
ReflectionOutput,
|
@@ -33,6 +35,8 @@ __all__ = [
|
|
33
35
|
'Task',
|
34
36
|
'TaskOutput',
|
35
37
|
'ReflectionOutput',
|
38
|
+
'AutoAgents',
|
39
|
+
'Memory',
|
36
40
|
'display_interaction',
|
37
41
|
'display_self_reflection',
|
38
42
|
'display_instruction',
|
praisonaiagents/agent/agent.py
CHANGED
@@ -162,7 +162,7 @@ class Agent:
|
|
162
162
|
max_iter: int = 20,
|
163
163
|
max_rpm: Optional[int] = None,
|
164
164
|
max_execution_time: Optional[int] = None,
|
165
|
-
memory:
|
165
|
+
memory: Optional[Any] = None,
|
166
166
|
verbose: bool = True,
|
167
167
|
allow_delegation: bool = False,
|
168
168
|
step_callback: Optional[Any] = None,
|
@@ -178,7 +178,7 @@ class Agent:
|
|
178
178
|
knowledge_sources: Optional[List[Any]] = None,
|
179
179
|
use_system_prompt: Optional[bool] = True,
|
180
180
|
markdown: bool = True,
|
181
|
-
self_reflect:
|
181
|
+
self_reflect: bool = False,
|
182
182
|
max_reflect: int = 3,
|
183
183
|
min_reflect: int = 1,
|
184
184
|
reflect_llm: Optional[str] = None
|
@@ -733,4 +733,8 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
|
|
733
733
|
return None
|
734
734
|
except Exception as e:
|
735
735
|
display_error(f"Error in _achat_completion: {e}")
|
736
|
-
return None
|
736
|
+
return None
|
737
|
+
|
738
|
+
def run(self):
|
739
|
+
"""Alias for start() method"""
|
740
|
+
return self.start()
|
praisonaiagents/agents/agents.py
CHANGED
@@ -13,6 +13,9 @@ from ..task.task import Task
|
|
13
13
|
from ..process.process import Process, LoopItems
|
14
14
|
import asyncio
|
15
15
|
|
16
|
+
# Set up logger
|
17
|
+
logger = logging.getLogger(__name__)
|
18
|
+
|
16
19
|
def encode_file_to_base64(file_path: str) -> str:
|
17
20
|
"""Base64-encode a file."""
|
18
21
|
import base64
|
@@ -41,7 +44,7 @@ def process_video(video_path: str, seconds_per_frame=2):
|
|
41
44
|
return base64_frames
|
42
45
|
|
43
46
|
class PraisonAIAgents:
|
44
|
-
def __init__(self, agents, tasks=None, verbose=0, completion_checker=None, max_retries=5, process="sequential", manager_llm=None):
|
47
|
+
def __init__(self, agents, tasks=None, verbose=0, completion_checker=None, max_retries=5, process="sequential", manager_llm=None, memory=False, memory_config=None, embedder=None):
|
45
48
|
if not agents:
|
46
49
|
raise ValueError("At least one agent must be provided")
|
47
50
|
|
@@ -57,8 +60,21 @@ class PraisonAIAgents:
|
|
57
60
|
|
58
61
|
# Check for manager_llm in environment variable if not provided
|
59
62
|
self.manager_llm = manager_llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
|
63
|
+
|
64
|
+
# Set logger level based on verbose
|
65
|
+
if verbose >= 5:
|
66
|
+
logger.setLevel(logging.INFO)
|
67
|
+
else:
|
68
|
+
logger.setLevel(logging.WARNING)
|
69
|
+
|
70
|
+
# Also set third-party loggers to WARNING
|
71
|
+
logging.getLogger('chromadb').setLevel(logging.WARNING)
|
72
|
+
logging.getLogger('openai').setLevel(logging.WARNING)
|
73
|
+
logging.getLogger('httpx').setLevel(logging.WARNING)
|
74
|
+
logging.getLogger('httpcore').setLevel(logging.WARNING)
|
75
|
+
|
60
76
|
if self.verbose:
|
61
|
-
|
77
|
+
logger.info(f"Using model {self.manager_llm} for manager")
|
62
78
|
|
63
79
|
# If no tasks provided, generate them from agents
|
64
80
|
if tasks is None:
|
@@ -66,12 +82,12 @@ class PraisonAIAgents:
|
|
66
82
|
for agent in self.agents:
|
67
83
|
task = agent.generate_task()
|
68
84
|
tasks.append(task)
|
69
|
-
|
85
|
+
logger.info(f"Auto-generated {len(tasks)} tasks from agents")
|
70
86
|
else:
|
71
87
|
# Validate tasks for backward compatibility
|
72
88
|
if not tasks:
|
73
89
|
raise ValueError("If tasks are provided, at least one task must be present")
|
74
|
-
|
90
|
+
logger.info(f"Using {len(tasks)} provided tasks")
|
75
91
|
|
76
92
|
# Add tasks and set their status
|
77
93
|
for task in tasks:
|
@@ -87,9 +103,65 @@ class PraisonAIAgents:
|
|
87
103
|
if tasks[i + 1].context is None:
|
88
104
|
tasks[i + 1].context = []
|
89
105
|
tasks[i + 1].context.append(tasks[i])
|
90
|
-
|
106
|
+
logger.info("Set up sequential flow with automatic context passing")
|
91
107
|
|
92
108
|
self._state = {} # Add state storage at PraisonAIAgents level
|
109
|
+
|
110
|
+
# Initialize memory system
|
111
|
+
self.shared_memory = None
|
112
|
+
if memory:
|
113
|
+
try:
|
114
|
+
from ..memory.memory import Memory
|
115
|
+
|
116
|
+
# Get memory config from parameter or first task
|
117
|
+
mem_cfg = memory_config
|
118
|
+
if not mem_cfg:
|
119
|
+
mem_cfg = next((t.config.get('memory_config') for t in tasks if hasattr(t, 'config') and t.config), None)
|
120
|
+
|
121
|
+
# Set default memory config if none provided
|
122
|
+
if not mem_cfg:
|
123
|
+
mem_cfg = {
|
124
|
+
"provider": "rag",
|
125
|
+
"use_embedding": True,
|
126
|
+
"storage": {
|
127
|
+
"type": "sqlite",
|
128
|
+
"path": "./.praison/memory.db"
|
129
|
+
},
|
130
|
+
"rag_db_path": "./.praison/chroma_db"
|
131
|
+
}
|
132
|
+
|
133
|
+
# Add embedder config if provided
|
134
|
+
if embedder:
|
135
|
+
if isinstance(embedder, dict):
|
136
|
+
mem_cfg = mem_cfg or {}
|
137
|
+
mem_cfg["embedder"] = embedder
|
138
|
+
else:
|
139
|
+
# Handle direct embedder function
|
140
|
+
mem_cfg = mem_cfg or {}
|
141
|
+
mem_cfg["embedder_function"] = embedder
|
142
|
+
|
143
|
+
if mem_cfg:
|
144
|
+
# Pass verbose level to Memory
|
145
|
+
self.shared_memory = Memory(config=mem_cfg, verbose=verbose)
|
146
|
+
if verbose >= 5:
|
147
|
+
logger.info("Initialized shared memory for PraisonAIAgents")
|
148
|
+
|
149
|
+
# Distribute memory to tasks
|
150
|
+
for task in tasks:
|
151
|
+
if not task.memory:
|
152
|
+
task.memory = self.shared_memory
|
153
|
+
if verbose >= 5:
|
154
|
+
logger.info(f"Assigned shared memory to task {task.id}")
|
155
|
+
|
156
|
+
except Exception as e:
|
157
|
+
logger.error(f"Failed to initialize shared memory: {e}")
|
158
|
+
|
159
|
+
# Update tasks with shared memory
|
160
|
+
if self.shared_memory:
|
161
|
+
for task in tasks:
|
162
|
+
if not task.memory:
|
163
|
+
task.memory = self.shared_memory
|
164
|
+
logger.info(f"Assigned shared memory to task {task.id}")
|
93
165
|
|
94
166
|
def add_task(self, task):
|
95
167
|
task_id = self.task_id_counter
|
@@ -142,7 +214,7 @@ class PraisonAIAgents:
|
|
142
214
|
task_prompt = f"""
|
143
215
|
You need to do the following task: {task.description}.
|
144
216
|
Expected Output: {task.expected_output}.
|
145
|
-
|
217
|
+
"""
|
146
218
|
if task.context:
|
147
219
|
context_results = ""
|
148
220
|
for context_task in task.context:
|
@@ -157,8 +229,8 @@ Here are the results of previous tasks that might be useful:\n
|
|
157
229
|
task_prompt += "Please provide only the final result of your work. Do not add any conversation or extra explanation."
|
158
230
|
|
159
231
|
if self.verbose >= 2:
|
160
|
-
|
161
|
-
|
232
|
+
logger.info(f"Executing task {task_id}: {task.description} using {executor_agent.name}")
|
233
|
+
logger.debug(f"Starting execution of task {task_id} with prompt:\n{task_prompt}")
|
162
234
|
|
163
235
|
if task.images:
|
164
236
|
def _get_multimodal_message(text_prompt, images):
|
@@ -223,8 +295,8 @@ Here are the results of previous tasks that might be useful:\n
|
|
223
295
|
task_output.json_dict = parsed
|
224
296
|
task_output.output_format = "JSON"
|
225
297
|
except:
|
226
|
-
|
227
|
-
|
298
|
+
logger.warning(f"Warning: Could not parse output of task {task_id} as JSON")
|
299
|
+
logger.debug(f"Output that failed JSON parsing: {agent_output}")
|
228
300
|
|
229
301
|
if task.output_pydantic:
|
230
302
|
cleaned = self.clean_json_output(agent_output)
|
@@ -234,8 +306,8 @@ Here are the results of previous tasks that might be useful:\n
|
|
234
306
|
task_output.pydantic = pyd_obj
|
235
307
|
task_output.output_format = "Pydantic"
|
236
308
|
except:
|
237
|
-
|
238
|
-
|
309
|
+
logger.warning(f"Warning: Could not parse output of task {task_id} as Pydantic Model")
|
310
|
+
logger.debug(f"Output that failed Pydantic parsing: {agent_output}")
|
239
311
|
|
240
312
|
task.result = task_output
|
241
313
|
return task_output
|
@@ -250,37 +322,53 @@ Here are the results of previous tasks that might be useful:\n
|
|
250
322
|
return
|
251
323
|
task = self.tasks[task_id]
|
252
324
|
if task.status == "completed":
|
253
|
-
|
325
|
+
logger.info(f"Task with ID {task_id} is already completed")
|
254
326
|
return
|
255
327
|
|
256
328
|
retries = 0
|
257
329
|
while task.status != "completed" and retries < self.max_retries:
|
258
|
-
|
330
|
+
logger.debug(f"Attempt {retries+1} for task {task_id}")
|
259
331
|
if task.status in ["not started", "in progress"]:
|
260
332
|
task_output = await self.aexecute_task(task_id)
|
261
333
|
if task_output and self.completion_checker(task, task_output.raw):
|
262
334
|
task.status = "completed"
|
263
|
-
|
335
|
+
# Run execute_callback for memory operations
|
336
|
+
try:
|
264
337
|
await task.execute_callback(task_output)
|
338
|
+
except Exception as e:
|
339
|
+
logger.error(f"Error executing memory callback for task {task_id}: {e}")
|
340
|
+
logger.exception(e)
|
341
|
+
|
342
|
+
# Run task callback if exists
|
343
|
+
if task.callback:
|
344
|
+
try:
|
345
|
+
if asyncio.iscoroutinefunction(task.callback):
|
346
|
+
await task.callback(task_output)
|
347
|
+
else:
|
348
|
+
task.callback(task_output)
|
349
|
+
except Exception as e:
|
350
|
+
logger.error(f"Error executing task callback for task {task_id}: {e}")
|
351
|
+
logger.exception(e)
|
352
|
+
|
265
353
|
self.save_output_to_file(task, task_output)
|
266
354
|
if self.verbose >= 1:
|
267
|
-
|
355
|
+
logger.info(f"Task {task_id} completed successfully.")
|
268
356
|
else:
|
269
357
|
task.status = "in progress"
|
270
358
|
if self.verbose >= 1:
|
271
|
-
|
359
|
+
logger.info(f"Task {task_id} not completed, retrying")
|
272
360
|
await asyncio.sleep(1)
|
273
361
|
retries += 1
|
274
362
|
else:
|
275
363
|
if task.status == "failed":
|
276
|
-
|
364
|
+
logger.info("Task is failed, resetting to in-progress for another try...")
|
277
365
|
task.status = "in progress"
|
278
366
|
else:
|
279
|
-
|
367
|
+
logger.info("Invalid Task status")
|
280
368
|
break
|
281
369
|
|
282
370
|
if retries == self.max_retries and task.status != "completed":
|
283
|
-
|
371
|
+
logger.info(f"Task {task_id} failed after {self.max_retries} retries.")
|
284
372
|
|
285
373
|
async def arun_all_tasks(self):
|
286
374
|
"""Async version of run_all_tasks method"""
|
@@ -328,7 +416,7 @@ Here are the results of previous tasks that might be useful:\n
|
|
328
416
|
with open(task.output_file, "w") as f:
|
329
417
|
f.write(str(task_output))
|
330
418
|
if self.verbose >= 1:
|
331
|
-
|
419
|
+
logger.info(f"Task output saved to {task.output_file}")
|
332
420
|
except Exception as e:
|
333
421
|
display_error(f"Error saving task output to file: {e}")
|
334
422
|
|
@@ -339,6 +427,15 @@ Here are the results of previous tasks that might be useful:\n
|
|
339
427
|
return
|
340
428
|
task = self.tasks[task_id]
|
341
429
|
|
430
|
+
logger.info(f"Starting execution of task {task_id}")
|
431
|
+
logger.info(f"Task config: {task.config}")
|
432
|
+
|
433
|
+
# Initialize memory before task execution
|
434
|
+
if not task.memory:
|
435
|
+
task.memory = task.initialize_memory()
|
436
|
+
|
437
|
+
logger.info(f"Task memory status: {'Initialized' if task.memory else 'Not initialized'}")
|
438
|
+
|
342
439
|
# Only import multimodal dependencies if task has images
|
343
440
|
if task.images and task.status == "not started":
|
344
441
|
try:
|
@@ -371,11 +468,20 @@ Expected Output: {task.expected_output}.
|
|
371
468
|
Here are the results of previous tasks that might be useful:\n
|
372
469
|
{context_results}
|
373
470
|
"""
|
471
|
+
# Add memory context if available
|
472
|
+
if task.memory:
|
473
|
+
try:
|
474
|
+
memory_context = task.memory.build_context_for_task(task.description)
|
475
|
+
if memory_context:
|
476
|
+
task_prompt += f"\n\nRelevant memory context:\n{memory_context}"
|
477
|
+
except Exception as e:
|
478
|
+
logger.error(f"Error getting memory context: {e}")
|
479
|
+
|
374
480
|
task_prompt += "Please provide only the final result of your work. Do not add any conversation or extra explanation."
|
375
481
|
|
376
482
|
if self.verbose >= 2:
|
377
|
-
|
378
|
-
|
483
|
+
logger.info(f"Executing task {task_id}: {task.description} using {executor_agent.name}")
|
484
|
+
logger.debug(f"Starting execution of task {task_id} with prompt:\n{task_prompt}")
|
379
485
|
|
380
486
|
if task.images:
|
381
487
|
def _get_multimodal_message(text_prompt, images):
|
@@ -425,6 +531,17 @@ Here are the results of previous tasks that might be useful:\n
|
|
425
531
|
)
|
426
532
|
|
427
533
|
if agent_output:
|
534
|
+
# Store the response in memory
|
535
|
+
if task.memory:
|
536
|
+
try:
|
537
|
+
task.store_in_memory(
|
538
|
+
content=agent_output,
|
539
|
+
agent_name=executor_agent.name,
|
540
|
+
task_id=task_id
|
541
|
+
)
|
542
|
+
except Exception as e:
|
543
|
+
logger.error(f"Failed to store agent output in memory: {e}")
|
544
|
+
|
428
545
|
task_output = TaskOutput(
|
429
546
|
description=task.description,
|
430
547
|
summary=task.description[:10],
|
@@ -440,8 +557,8 @@ Here are the results of previous tasks that might be useful:\n
|
|
440
557
|
task_output.json_dict = parsed
|
441
558
|
task_output.output_format = "JSON"
|
442
559
|
except:
|
443
|
-
|
444
|
-
|
560
|
+
logger.warning(f"Warning: Could not parse output of task {task_id} as JSON")
|
561
|
+
logger.debug(f"Output that failed JSON parsing: {agent_output}")
|
445
562
|
|
446
563
|
if task.output_pydantic:
|
447
564
|
cleaned = self.clean_json_output(agent_output)
|
@@ -451,8 +568,8 @@ Here are the results of previous tasks that might be useful:\n
|
|
451
568
|
task_output.pydantic = pyd_obj
|
452
569
|
task_output.output_format = "Pydantic"
|
453
570
|
except:
|
454
|
-
|
455
|
-
|
571
|
+
logger.warning(f"Warning: Could not parse output of task {task_id} as Pydantic Model")
|
572
|
+
logger.debug(f"Output that failed Pydantic parsing: {agent_output}")
|
456
573
|
|
457
574
|
task.result = task_output
|
458
575
|
return task_output
|
@@ -467,37 +584,54 @@ Here are the results of previous tasks that might be useful:\n
|
|
467
584
|
return
|
468
585
|
task = self.tasks[task_id]
|
469
586
|
if task.status == "completed":
|
470
|
-
|
587
|
+
logger.info(f"Task with ID {task_id} is already completed")
|
471
588
|
return
|
472
589
|
|
473
590
|
retries = 0
|
474
591
|
while task.status != "completed" and retries < self.max_retries:
|
475
|
-
|
592
|
+
logger.debug(f"Attempt {retries+1} for task {task_id}")
|
476
593
|
if task.status in ["not started", "in progress"]:
|
477
594
|
task_output = self.execute_task(task_id)
|
478
595
|
if task_output and self.completion_checker(task, task_output.raw):
|
479
596
|
task.status = "completed"
|
597
|
+
# Run execute_callback for memory operations
|
598
|
+
try:
|
599
|
+
loop = asyncio.get_event_loop()
|
600
|
+
loop.run_until_complete(task.execute_callback(task_output))
|
601
|
+
except Exception as e:
|
602
|
+
logger.error(f"Error executing memory callback for task {task_id}: {e}")
|
603
|
+
logger.exception(e)
|
604
|
+
|
605
|
+
# Run task callback if exists
|
480
606
|
if task.callback:
|
481
|
-
|
607
|
+
try:
|
608
|
+
if asyncio.iscoroutinefunction(task.callback):
|
609
|
+
loop.run_until_complete(task.callback(task_output))
|
610
|
+
else:
|
611
|
+
task.callback(task_output)
|
612
|
+
except Exception as e:
|
613
|
+
logger.error(f"Error executing task callback for task {task_id}: {e}")
|
614
|
+
logger.exception(e)
|
615
|
+
|
482
616
|
self.save_output_to_file(task, task_output)
|
483
617
|
if self.verbose >= 1:
|
484
|
-
|
618
|
+
logger.info(f"Task {task_id} completed successfully.")
|
485
619
|
else:
|
486
620
|
task.status = "in progress"
|
487
621
|
if self.verbose >= 1:
|
488
|
-
|
622
|
+
logger.info(f"Task {task_id} not completed, retrying")
|
489
623
|
time.sleep(1)
|
490
624
|
retries += 1
|
491
625
|
else:
|
492
626
|
if task.status == "failed":
|
493
|
-
|
627
|
+
logger.info("Task is failed, resetting to in-progress for another try...")
|
494
628
|
task.status = "in progress"
|
495
629
|
else:
|
496
|
-
|
630
|
+
logger.info("Invalid Task status")
|
497
631
|
break
|
498
632
|
|
499
633
|
if retries == self.max_retries and task.status != "completed":
|
500
|
-
|
634
|
+
logger.info(f"Task {task_id} failed after {self.max_retries} retries.")
|
501
635
|
|
502
636
|
def run_all_tasks(self):
|
503
637
|
"""Synchronous version of run_all_tasks method"""
|