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.
@@ -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',
@@ -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: bool = True,
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: Optional[bool] = None,
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()
@@ -1,4 +1,5 @@
1
1
  """Agents module for managing multiple AI agents"""
2
2
  from .agents import PraisonAIAgents
3
+ from .autoagents import AutoAgents
3
4
 
4
- __all__ = ['PraisonAIAgents']
5
+ __all__ = ['PraisonAIAgents', 'AutoAgents']
@@ -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
- logging.info(f"Using model {self.manager_llm} for manager")
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
- logging.info(f"Auto-generated {len(tasks)} tasks from agents")
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
- logging.info(f"Using {len(tasks)} provided tasks")
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
- logging.info("Set up sequential flow with automatic context passing")
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
- logging.info(f"Executing task {task_id}: {task.description} using {executor_agent.name}")
161
- logging.debug(f"Starting execution of task {task_id} with prompt:\n{task_prompt}")
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
- logging.warning(f"Warning: Could not parse output of task {task_id} as JSON")
227
- logging.debug(f"Output that failed JSON parsing: {agent_output}")
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
- logging.warning(f"Warning: Could not parse output of task {task_id} as Pydantic Model")
238
- logging.debug(f"Output that failed Pydantic parsing: {agent_output}")
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
- logging.info(f"Task with ID {task_id} is already completed")
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
- logging.debug(f"Attempt {retries+1} for task {task_id}")
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
- if task.callback:
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
- logging.info(f"Task {task_id} completed successfully.")
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
- logging.info(f"Task {task_id} not completed, retrying")
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
- logging.info("Task is failed, resetting to in-progress for another try...")
364
+ logger.info("Task is failed, resetting to in-progress for another try...")
277
365
  task.status = "in progress"
278
366
  else:
279
- logging.info("Invalid Task status")
367
+ logger.info("Invalid Task status")
280
368
  break
281
369
 
282
370
  if retries == self.max_retries and task.status != "completed":
283
- logging.info(f"Task {task_id} failed after {self.max_retries} retries.")
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
- logging.info(f"Task output saved to {task.output_file}")
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
- logging.info(f"Executing task {task_id}: {task.description} using {executor_agent.name}")
378
- logging.debug(f"Starting execution of task {task_id} with prompt:\n{task_prompt}")
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
- logging.warning(f"Warning: Could not parse output of task {task_id} as JSON")
444
- logging.debug(f"Output that failed JSON parsing: {agent_output}")
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
- logging.warning(f"Warning: Could not parse output of task {task_id} as Pydantic Model")
455
- logging.debug(f"Output that failed Pydantic parsing: {agent_output}")
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
- logging.info(f"Task with ID {task_id} is already completed")
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
- logging.debug(f"Attempt {retries+1} for task {task_id}")
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
- task.callback(task_output)
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
- logging.info(f"Task {task_id} completed successfully.")
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
- logging.info(f"Task {task_id} not completed, retrying")
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
- logging.info("Task is failed, resetting to in-progress for another try...")
627
+ logger.info("Task is failed, resetting to in-progress for another try...")
494
628
  task.status = "in progress"
495
629
  else:
496
- logging.info("Invalid Task status")
630
+ logger.info("Invalid Task status")
497
631
  break
498
632
 
499
633
  if retries == self.max_retries and task.status != "completed":
500
- logging.info(f"Task {task_id} failed after {self.max_retries} retries.")
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"""