praisonaiagents 0.0.28__py3-none-any.whl → 0.0.30__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,318 +0,0 @@
1
- import os
2
- import time
3
- import json
4
- import logging
5
- from typing import Any, Dict, Optional
6
- from pydantic import BaseModel
7
- from rich.text import Text
8
- from rich.panel import Panel
9
- from rich.console import Console
10
- from ..main import display_error, TaskOutput, error_logs, client
11
- from ..agent.agent import Agent
12
- from ..task.task import Task
13
-
14
- class Agents:
15
- def __init__(self, agents, tasks, verbose=0, completion_checker=None, max_retries=5, process="sequential", manager_llm=None):
16
- self.agents = agents
17
- self.tasks = {}
18
- if max_retries < 3:
19
- max_retries = 3
20
- self.completion_checker = completion_checker if completion_checker else self.default_completion_checker
21
- self.task_id_counter = 0
22
- self.verbose = verbose
23
- self.max_retries = max_retries
24
- self.process = process
25
- self.manager_llm = manager_llm
26
- for task in tasks:
27
- self.add_task(task)
28
- task.status = "not started"
29
-
30
- def add_task(self, task):
31
- task_id = self.task_id_counter
32
- task.id = task_id
33
- self.tasks[task_id] = task
34
- self.task_id_counter += 1
35
- return task_id
36
-
37
- def clean_json_output(self, output: str) -> str:
38
- cleaned = output.strip()
39
- if cleaned.startswith("```json"):
40
- cleaned = cleaned[len("```json"):].strip()
41
- if cleaned.startswith("```"):
42
- cleaned = cleaned[len("```"):].strip()
43
- if cleaned.endswith("```"):
44
- cleaned = cleaned[:-3].strip()
45
- return cleaned
46
-
47
- def default_completion_checker(self, task, agent_output):
48
- if task.output_json and task.result and task.result.json_dict:
49
- return True
50
- if task.output_pydantic and task.result and task.result.pydantic:
51
- return True
52
- return len(agent_output.strip()) > 0
53
-
54
- def execute_task(self, task_id):
55
- if task_id not in self.tasks:
56
- display_error(f"Error: Task with ID {task_id} does not exist")
57
- return
58
- task = self.tasks[task_id]
59
- if task.status == "not started":
60
- task.status = "in progress"
61
-
62
- executor_agent = task.agent
63
-
64
- task_prompt = f"""
65
- You need to do the following task: {task.description}.
66
- Expected Output: {task.expected_output}.
67
- """
68
- if task.context:
69
- context_results = ""
70
- for context_task in task.context:
71
- if context_task.result:
72
- context_results += f"Result of previous task {context_task.name if context_task.name else context_task.description}: {context_task.result.raw}\n"
73
- else:
74
- context_results += f"Previous task {context_task.name if context_task.name else context_task.description} had no result.\n"
75
- task_prompt += f"""
76
- Here are the results of previous tasks that might be useful:\n
77
- {context_results}
78
- """
79
- task_prompt += "Please provide only the final result of your work. Do not add any conversation or extra explanation."
80
-
81
- if self.verbose >= 2:
82
- logging.info(f"Executing task {task_id}: {task.description} using {executor_agent.name}")
83
- logging.debug(f"Starting execution of task {task_id} with prompt:\n{task_prompt}")
84
- agent_output = executor_agent.chat(task_prompt, tools=task.tools)
85
- if agent_output:
86
- task_output = TaskOutput(
87
- description=task.description,
88
- summary=task.description[:10],
89
- raw=agent_output,
90
- agent=executor_agent.name,
91
- output_format="RAW"
92
- )
93
-
94
- if task.output_json:
95
- cleaned = self.clean_json_output(agent_output)
96
- try:
97
- parsed = json.loads(cleaned)
98
- task_output.json_dict = parsed
99
- task_output.output_format = "JSON"
100
- except:
101
- logging.warning(f"Warning: Could not parse output of task {task_id} as JSON")
102
- logging.debug(f"Output that failed JSON parsing: {agent_output}")
103
-
104
- if task.output_pydantic:
105
- cleaned = self.clean_json_output(agent_output)
106
- try:
107
- parsed = json.loads(cleaned)
108
- pyd_obj = task.output_pydantic(**parsed)
109
- task_output.pydantic = pyd_obj
110
- task_output.output_format = "Pydantic"
111
- except:
112
- logging.warning(f"Warning: Could not parse output of task {task_id} as Pydantic Model")
113
- logging.debug(f"Output that failed Pydantic parsing: {agent_output}")
114
-
115
- task.result = task_output
116
- return task_output
117
- else:
118
- task.status = "failed"
119
- return None
120
-
121
- def save_output_to_file(self, task, task_output):
122
- if task.output_file:
123
- try:
124
- if task.create_directory:
125
- os.makedirs(os.path.dirname(task.output_file), exist_ok=True)
126
- with open(task.output_file, "w") as f:
127
- f.write(str(task_output))
128
- if self.verbose >= 1:
129
- logging.info(f"Task output saved to {task.output_file}")
130
- except Exception as e:
131
- display_error(f"Error saving task output to file: {e}")
132
-
133
- def run_task(self, task_id):
134
- if task_id not in self.tasks:
135
- display_error(f"Error: Task with ID {task_id} does not exist")
136
- return
137
- task = self.tasks[task_id]
138
- if task.status == "completed":
139
- logging.info(f"Task with ID {task_id} is already completed")
140
- return
141
-
142
- retries = 0
143
- while task.status != "completed" and retries < self.max_retries:
144
- logging.debug(f"Attempt {retries+1} for task {task_id}")
145
- if task.status in ["not started", "in progress"]:
146
- task_output = self.execute_task(task_id)
147
- if task_output and self.completion_checker(task, task_output.raw):
148
- task.status = "completed"
149
- if task.callback:
150
- task.callback(task_output)
151
- self.save_output_to_file(task, task_output)
152
- if self.verbose >= 1:
153
- logging.info(f"Task {task_id} completed successfully.")
154
- else:
155
- task.status = "in progress"
156
- if self.verbose >= 1:
157
- logging.info(f"Task {task_id} not completed, retrying")
158
- time.sleep(1)
159
- retries += 1
160
- else:
161
- if task.status == "failed":
162
- logging.info("Task is failed, resetting to in-progress for another try...")
163
- task.status = "in progress"
164
- else:
165
- logging.info("Invalid Task status")
166
- break
167
-
168
- if retries == self.max_retries and task.status != "completed":
169
- logging.info(f"Task {task_id} failed after {self.max_retries} retries.")
170
-
171
- def run_all_tasks(self):
172
- if self.process == "sequential":
173
- for task_id in self.tasks:
174
- if self.tasks[task_id].status != "completed":
175
- self.run_task(task_id)
176
- elif self.process == "hierarchical":
177
- logging.debug(f"Starting hierarchical task execution with {len(self.tasks)} tasks")
178
- manager_agent = Agent(
179
- name="Manager",
180
- role="Project manager",
181
- goal="Manage the entire flow of tasks and delegate them to the right agent",
182
- backstory="Expert project manager to coordinate tasks among agents",
183
- llm=self.manager_llm,
184
- verbose=self.verbose,
185
- markdown=True,
186
- self_reflect=False
187
- )
188
-
189
- class ManagerInstructions(BaseModel):
190
- task_id: int
191
- agent_name: str
192
- action: str
193
-
194
- manager_task = Task(
195
- name="manager_task",
196
- description="Decide the order of tasks and which agent executes them",
197
- expected_output="All tasks completed successfully",
198
- agent=manager_agent
199
- )
200
- manager_task_id = self.add_task(manager_task)
201
- logging.info(f"Created manager task with ID {manager_task_id}")
202
-
203
- completed_count = 0
204
- total_tasks = len(self.tasks) - 1
205
- logging.info(f"Need to complete {total_tasks} tasks (excluding manager task)")
206
-
207
- while completed_count < total_tasks:
208
- tasks_summary = []
209
- for tid, tk in self.tasks.items():
210
- if tk.name == "manager_task":
211
- continue
212
- task_info = {
213
- "task_id": tid,
214
- "name": tk.name,
215
- "description": tk.description,
216
- "status": tk.status if tk.status else "not started",
217
- "agent": tk.agent.name if tk.agent else "No agent"
218
- }
219
- tasks_summary.append(task_info)
220
- logging.info(f"Task {tid} status: {task_info}")
221
-
222
- manager_prompt = f"""
223
- Here is the current status of all tasks except yours (manager_task):
224
- {tasks_summary}
225
-
226
- Provide a JSON with the structure:
227
- {{
228
- "task_id": <int>,
229
- "agent_name": "<string>",
230
- "action": "<execute or stop>"
231
- }}
232
- """
233
-
234
- try:
235
- logging.info("Requesting manager instructions...")
236
- manager_response = client.beta.chat.completions.parse(
237
- model=self.manager_llm,
238
- messages=[
239
- {"role": "system", "content": manager_task.description},
240
- {"role": "user", "content": manager_prompt}
241
- ],
242
- temperature=0.7,
243
- response_format=ManagerInstructions
244
- )
245
- parsed_instructions = manager_response.choices[0].message.parsed
246
- logging.info(f"Manager instructions: {parsed_instructions}")
247
- except Exception as e:
248
- display_error(f"Manager parse error: {e}")
249
- logging.error(f"Manager parse error: {str(e)}", exc_info=True)
250
- break
251
-
252
- selected_task_id = parsed_instructions.task_id
253
- selected_agent_name = parsed_instructions.agent_name
254
- action = parsed_instructions.action
255
-
256
- logging.info(f"Manager selected task_id={selected_task_id}, agent={selected_agent_name}, action={action}")
257
-
258
- if action.lower() == "stop":
259
- logging.info("Manager decided to stop task execution")
260
- break
261
-
262
- if selected_task_id not in self.tasks:
263
- error_msg = f"Manager selected invalid task id {selected_task_id}"
264
- display_error(error_msg)
265
- logging.error(error_msg)
266
- break
267
-
268
- original_agent = self.tasks[selected_task_id].agent.name if self.tasks[selected_task_id].agent else "None"
269
- for a in self.agents:
270
- if a.name == selected_agent_name:
271
- self.tasks[selected_task_id].agent = a
272
- logging.info(f"Changed agent for task {selected_task_id} from {original_agent} to {selected_agent_name}")
273
- break
274
-
275
- if self.tasks[selected_task_id].status != "completed":
276
- logging.info(f"Starting execution of task {selected_task_id}")
277
- self.run_task(selected_task_id)
278
- logging.info(f"Finished execution of task {selected_task_id}, status: {self.tasks[selected_task_id].status}")
279
-
280
- if self.tasks[selected_task_id].status == "completed":
281
- completed_count += 1
282
- logging.info(f"Task {selected_task_id} completed. Total completed: {completed_count}/{total_tasks}")
283
-
284
- self.tasks[manager_task.id].status = "completed"
285
- if self.verbose >= 1:
286
- logging.info("All tasks completed under manager supervision.")
287
- logging.info("Hierarchical task execution finished")
288
-
289
- def get_task_status(self, task_id):
290
- if task_id in self.tasks:
291
- return self.tasks[task_id].status
292
- return None
293
-
294
- def get_all_tasks_status(self):
295
- return {task_id: self.tasks[task_id].status for task_id in self.tasks}
296
-
297
- def get_task_result(self, task_id):
298
- if task_id in self.tasks:
299
- return self.tasks[task_id].result
300
- return None
301
-
302
- def get_task_details(self, task_id):
303
- if task_id in self.tasks:
304
- return str(self.tasks[task_id])
305
- return None
306
-
307
- def get_agent_details(self, agent_name):
308
- agent = [task.agent for task in self.tasks.values() if task.agent and task.agent.name == agent_name]
309
- if agent:
310
- return str(agent[0])
311
- return None
312
-
313
- def start(self):
314
- self.run_all_tasks()
315
- return {
316
- "task_status": self.get_all_tasks_status(),
317
- "task_results": {task_id: self.get_task_result(task_id) for task_id in self.tasks}
318
- }
@@ -1,112 +0,0 @@
1
- import os
2
- import time
3
- import json
4
- import logging
5
- from typing import List, Optional, Dict, Any, Union, Literal, Type
6
- from openai import OpenAI
7
- from pydantic import BaseModel
8
- from rich import print
9
- from rich.console import Console
10
- from rich.panel import Panel
11
- from rich.text import Text
12
- from rich.markdown import Markdown
13
- from rich.logging import RichHandler
14
- from rich.live import Live
15
-
16
- LOGLEVEL = os.environ.get('LOGLEVEL', 'INFO').upper()
17
-
18
- logging.basicConfig(
19
- level=getattr(logging, LOGLEVEL, logging.INFO),
20
- format="%(asctime)s %(filename)s:%(lineno)d %(levelname)s %(message)s",
21
- datefmt="[%X]",
22
- handlers=[RichHandler(rich_tracebacks=True)]
23
- )
24
-
25
- # Global list to store error logs
26
- error_logs = []
27
-
28
- def display_interaction(message: str, response: str, markdown: bool = True, generation_time: Optional[float] = None):
29
- console = Console()
30
- if generation_time is not None:
31
- console.print(Text(f"Response generated in {generation_time:.1f}s", style="dim"))
32
- else:
33
- console.print(Text("Response Generation Complete", style="dim"))
34
-
35
- if markdown:
36
- console.print(Panel.fit(Markdown(message), title="Message", border_style="cyan"))
37
- console.print(Panel.fit(Markdown(response), title="Response", border_style="cyan"))
38
- else:
39
- console.print(Panel.fit(Text(message, style="bold green"), title="Message", border_style="cyan"))
40
- console.print(Panel.fit(Text(response, style="bold white"), title="Response", border_style="cyan"))
41
-
42
- def display_self_reflection(message: str):
43
- console = Console()
44
- console.print(Panel.fit(Text(message, style="bold yellow"), title="Self Reflection", border_style="magenta"))
45
-
46
- def display_instruction(message: str):
47
- console = Console()
48
- console.print(Panel.fit(Text(message, style="bold blue"), title="Instruction", border_style="cyan"))
49
-
50
- def display_tool_call(message: str):
51
- console = Console()
52
- console.print(Panel.fit(Text(message, style="bold cyan"), title="Tool Call", border_style="green"))
53
-
54
- def display_error(message: str):
55
- console = Console()
56
- console.print(Panel.fit(Text(message, style="bold red"), title="Error", border_style="red"))
57
- # Store errors
58
- error_logs.append(message)
59
-
60
- def display_generating(content: str = "", start_time: Optional[float] = None):
61
- elapsed_str = ""
62
- if start_time is not None:
63
- elapsed = time.time() - start_time
64
- elapsed_str = f" {elapsed:.1f}s"
65
- return Panel(Markdown(content), title=f"Generating...{elapsed_str}", border_style="green")
66
-
67
- def clean_triple_backticks(text: str) -> str:
68
- """Remove triple backticks and surrounding json fences from a string."""
69
- cleaned = text.strip()
70
- if cleaned.startswith("```json"):
71
- cleaned = cleaned[len("```json"):].strip()
72
- if cleaned.startswith("```"):
73
- cleaned = cleaned[len("```"):].strip()
74
- if cleaned.endswith("```"):
75
- cleaned = cleaned[:-3].strip()
76
- return cleaned
77
-
78
- class ReflectionOutput(BaseModel):
79
- reflection: str
80
- satisfactory: Literal["yes", "no"]
81
-
82
- client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
83
-
84
- class TaskOutput(BaseModel):
85
- description: str
86
- summary: Optional[str] = None
87
- raw: str
88
- pydantic: Optional[BaseModel] = None
89
- json_dict: Optional[Dict[str, Any]] = None
90
- agent: str
91
- output_format: Literal["RAW", "JSON", "Pydantic"] = "RAW"
92
-
93
- def json(self) -> Optional[str]:
94
- if self.output_format == "JSON" and self.json_dict:
95
- return json.dumps(self.json_dict)
96
- return None
97
-
98
- def to_dict(self) -> dict:
99
- output_dict = {}
100
- if self.json_dict:
101
- output_dict.update(self.json_dict)
102
- if self.pydantic:
103
- output_dict.update(self.pydantic.model_dump())
104
- return output_dict
105
-
106
- def __str__(self):
107
- if self.pydantic:
108
- return str(self.pydantic)
109
- elif self.json_dict:
110
- return json.dumps(self.json_dict)
111
- else:
112
- return self.raw
@@ -1,4 +0,0 @@
1
- """Task module for AI agent tasks"""
2
- from .task import Task
3
-
4
- __all__ = ['Task']
@@ -1,48 +0,0 @@
1
- import logging
2
- from typing import List, Optional, Dict, Any, Type
3
- from pydantic import BaseModel
4
- from ..main import TaskOutput
5
- from ..agent.agent import Agent
6
-
7
- class Task:
8
- def __init__(
9
- self,
10
- description: str,
11
- expected_output: str,
12
- agent: Optional[Agent] = None,
13
- name: Optional[str] = None,
14
- tools: Optional[List[Any]] = None,
15
- context: Optional[List["Task"]] = None,
16
- async_execution: Optional[bool] = False,
17
- config: Optional[Dict[str, Any]] = None,
18
- output_file: Optional[str] = None,
19
- output_json: Optional[Type[BaseModel]] = None,
20
- output_pydantic: Optional[Type[BaseModel]] = None,
21
- callback: Optional[Any] = None,
22
- status: str = "not started",
23
- result: Optional[TaskOutput] = None,
24
- create_directory: Optional[bool] = False,
25
- id: Optional[int] = None
26
- ):
27
- self.description = description
28
- self.expected_output = expected_output
29
- self.name = name
30
- self.agent = agent
31
- self.tools = tools if tools else []
32
- self.context = context if context else []
33
- self.async_execution = async_execution
34
- self.config = config if config else {}
35
- self.output_file = output_file
36
- self.output_json = output_json
37
- self.output_pydantic = output_pydantic
38
- self.callback = callback
39
- self.status = status
40
- self.result = result
41
- self.create_directory = create_directory
42
- self.id = id
43
-
44
- if self.output_json and self.output_pydantic:
45
- raise ValueError("Only one output type can be defined")
46
-
47
- def __str__(self):
48
- return f"Task(name='{self.name if self.name else 'None'}', description='{self.description}', agent='{self.agent.name if self.agent else 'None'}', status='{self.status}')"