praisonaiagents 0.0.14__py3-none-any.whl → 0.0.15__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,6 +10,7 @@ from rich.console import Console
10
10
  from ..main import display_error, TaskOutput, error_logs, client
11
11
  from ..agent.agent import Agent
12
12
  from ..task.task import Task
13
+ from ..process.process import Process
13
14
 
14
15
  class LoopItems(BaseModel):
15
16
  items: List[Any]
@@ -256,254 +257,24 @@ Expected Output: {task.expected_output}.
256
257
 
257
258
  def run_all_tasks(self):
258
259
  """Execute tasks based on execution mode"""
260
+ process = Process(
261
+ tasks=self.tasks,
262
+ agents=self.agents,
263
+ manager_llm=self.manager_llm,
264
+ verbose=self.verbose
265
+ )
266
+
259
267
  if self.process == "workflow":
260
- # Build workflow relationships first
261
- for task in self.tasks.values():
262
- if task.next_tasks:
263
- for next_task_name in task.next_tasks:
264
- next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
265
- if next_task:
266
- next_task.previous_tasks.append(task.name)
267
-
268
- # Find start task
269
- start_task = None
270
- for task_id, task in self.tasks.items():
271
- if task.is_start:
272
- start_task = task
273
- break
274
-
275
- if not start_task:
276
- start_task = list(self.tasks.values())[0]
277
- logging.info("No start task marked, using first task")
278
-
279
- current_task = start_task
280
- visited_tasks = set()
281
- loop_data = {} # Store loop-specific data
282
-
283
- while current_task and current_task.id not in visited_tasks:
284
- task_id = current_task.id
285
- logging.info(f"Executing workflow task: {current_task.name if current_task.name else task_id}")
286
-
287
- # Add context from previous tasks to description
288
- if current_task.previous_tasks or current_task.context:
289
- context = "\nInput data from previous tasks:"
290
-
291
- # Add data from previous tasks in workflow
292
- for prev_name in current_task.previous_tasks:
293
- prev_task = next((t for t in self.tasks.values() if t.name == prev_name), None)
294
- if prev_task and prev_task.result:
295
- # Handle loop data
296
- if current_task.task_type == "loop":
297
- # create a loop manager Agent
298
- loop_manager = Agent(
299
- name="Loop Manager",
300
- role="Loop data processor",
301
- goal="Process loop data and convert it to list format",
302
- backstory="Expert at handling loop data and converting it to proper format",
303
- llm=self.manager_llm,
304
- verbose=self.verbose,
305
- markdown=True
306
- )
307
-
308
- # get the loop data convert it to list using calling Agent class chat
309
- loop_prompt = f"""
310
- Process this data into a list format:
311
- {prev_task.result.raw}
312
-
313
- Return a JSON object with an 'items' array containing the items to process.
314
- """
315
- loop_data_str = loop_manager.chat(
316
- prompt=loop_prompt,
317
- output_json=LoopItems
318
- )
319
-
320
- try:
321
- # The response will already be parsed into LoopItems model
322
- loop_data[f"loop_{current_task.name}"] = {
323
- "items": loop_data_str.items,
324
- "index": 0,
325
- "remaining": len(loop_data_str.items)
326
- }
327
- context += f"\nCurrent loop item: {loop_data_str.items[0]}"
328
- except Exception as e:
329
- display_error(f"Failed to process loop data: {e}")
330
- context += f"\n{prev_name}: {prev_task.result.raw}"
331
- else:
332
- context += f"\n{prev_name}: {prev_task.result.raw}"
333
-
334
- # Add data from context tasks
335
- if current_task.context:
336
- for ctx_task in current_task.context:
337
- if ctx_task.result and ctx_task.name != current_task.name:
338
- context += f"\n{ctx_task.name}: {ctx_task.result.raw}"
339
-
340
- # Update task description with context
341
- current_task.description = current_task.description + context
342
-
343
- # Execute task using existing run_task method
268
+ for task_id in process.workflow():
344
269
  self.run_task(task_id)
345
- visited_tasks.add(task_id)
346
-
347
- # Handle loop progression
348
- if current_task.task_type == "loop":
349
- loop_key = f"loop_{current_task.name}"
350
- if loop_key in loop_data:
351
- loop_info = loop_data[loop_key]
352
- loop_info["index"] += 1
353
- has_more = loop_info["remaining"] > 0
354
-
355
- # Update result to trigger correct condition
356
- if current_task.result:
357
- result = current_task.result.raw
358
- if has_more:
359
- result += "\nmore"
360
- else:
361
- result += "\ndone"
362
- current_task.result.raw = result
363
-
364
- # Determine next task based on result
365
- next_task = None
366
- if current_task.result:
367
- if current_task.task_type in ["decision", "loop"]:
368
- result = current_task.result.raw.lower()
369
- # Check conditions
370
- for condition, tasks in current_task.condition.items():
371
- if condition.lower() in result and tasks:
372
- next_task_name = tasks[0]
373
- next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
374
- # For loops, allow revisiting the same task
375
- if next_task and next_task.id == current_task.id:
376
- visited_tasks.discard(current_task.id)
377
- break
378
-
379
- if not next_task and current_task.next_tasks:
380
- next_task_name = current_task.next_tasks[0]
381
- next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
382
-
383
- current_task = next_task
384
- if not current_task:
385
- logging.info("Workflow execution completed")
386
- break
387
-
388
270
  elif self.process == "sequential":
389
- # Keep original sequential execution
390
- for task_id in self.tasks:
391
- if self.tasks[task_id].status != "completed":
392
- self.run_task(task_id)
393
-
271
+ for task_id in process.sequential():
272
+ self.run_task(task_id)
394
273
  elif self.process == "hierarchical":
395
- # Keep original hierarchical execution
396
- logging.debug(f"Starting hierarchical task execution with {len(self.tasks)} tasks")
397
- manager_agent = Agent(
398
- name="Manager",
399
- role="Project manager",
400
- goal="Manage the entire flow of tasks and delegate them to the right agent",
401
- backstory="Expert project manager to coordinate tasks among agents",
402
- llm=self.manager_llm,
403
- verbose=self.verbose,
404
- markdown=True,
405
- self_reflect=False
406
- )
407
-
408
- class ManagerInstructions(BaseModel):
409
- task_id: int
410
- agent_name: str
411
- action: str
412
-
413
- manager_task = Task(
414
- name="manager_task",
415
- description="Decide the order of tasks and which agent executes them",
416
- expected_output="All tasks completed successfully",
417
- agent=manager_agent
418
- )
419
- manager_task_id = self.add_task(manager_task)
420
- logging.info(f"Created manager task with ID {manager_task_id}")
421
-
422
- completed_count = 0
423
- total_tasks = len(self.tasks) - 1
424
- logging.info(f"Need to complete {total_tasks} tasks (excluding manager task)")
425
-
426
- while completed_count < total_tasks:
427
- tasks_summary = []
428
- for tid, tk in self.tasks.items():
429
- if tk.name == "manager_task":
430
- continue
431
- task_info = {
432
- "task_id": tid,
433
- "name": tk.name,
434
- "description": tk.description,
435
- "status": tk.status if tk.status else "not started",
436
- "agent": tk.agent.name if tk.agent else "No agent"
437
- }
438
- tasks_summary.append(task_info)
439
- logging.info(f"Task {tid} status: {task_info}")
440
-
441
- manager_prompt = f"""
442
- Here is the current status of all tasks except yours (manager_task):
443
- {tasks_summary}
444
-
445
- Provide a JSON with the structure:
446
- {{
447
- "task_id": <int>,
448
- "agent_name": "<string>",
449
- "action": "<execute or stop>"
450
- }}
451
- """
452
-
453
- try:
454
- logging.info("Requesting manager instructions...")
455
- manager_response = client.beta.chat.completions.parse(
456
- model=self.manager_llm,
457
- messages=[
458
- {"role": "system", "content": manager_task.description},
459
- {"role": "user", "content": manager_prompt}
460
- ],
461
- temperature=0.7,
462
- response_format=ManagerInstructions
463
- )
464
- parsed_instructions = manager_response.choices[0].message.parsed
465
- logging.info(f"Manager instructions: {parsed_instructions}")
466
- except Exception as e:
467
- display_error(f"Manager parse error: {e}")
468
- logging.error(f"Manager parse error: {str(e)}", exc_info=True)
469
- break
470
-
471
- selected_task_id = parsed_instructions.task_id
472
- selected_agent_name = parsed_instructions.agent_name
473
- action = parsed_instructions.action
474
-
475
- logging.info(f"Manager selected task_id={selected_task_id}, agent={selected_agent_name}, action={action}")
476
-
477
- if action.lower() == "stop":
478
- logging.info("Manager decided to stop task execution")
479
- break
480
-
481
- if selected_task_id not in self.tasks:
482
- error_msg = f"Manager selected invalid task id {selected_task_id}"
483
- display_error(error_msg)
484
- logging.error(error_msg)
485
- break
486
-
487
- original_agent = self.tasks[selected_task_id].agent.name if self.tasks[selected_task_id].agent else "None"
488
- for a in self.agents:
489
- if a.name == selected_agent_name:
490
- self.tasks[selected_task_id].agent = a
491
- logging.info(f"Changed agent for task {selected_task_id} from {original_agent} to {selected_agent_name}")
492
- break
493
-
494
- if self.tasks[selected_task_id].status != "completed":
495
- logging.info(f"Starting execution of task {selected_task_id}")
496
- self.run_task(selected_task_id)
497
- logging.info(f"Finished execution of task {selected_task_id}, status: {self.tasks[selected_task_id].status}")
498
-
499
- if self.tasks[selected_task_id].status == "completed":
500
- completed_count += 1
501
- logging.info(f"Task {selected_task_id} completed. Total completed: {completed_count}/{total_tasks}")
502
-
503
- self.tasks[manager_task.id].status = "completed"
504
- if self.verbose >= 1:
505
- logging.info("All tasks completed under manager supervision.")
506
- logging.info("Hierarchical task execution finished")
274
+ for task_id in process.hierarchical():
275
+ if isinstance(task_id, Task):
276
+ task_id = self.add_task(task_id)
277
+ self.run_task(task_id)
507
278
 
508
279
  def get_task_status(self, task_id):
509
280
  if task_id in self.tasks:
@@ -0,0 +1,3 @@
1
+ from .process import Process
2
+
3
+ __all__ = ['Process']
@@ -0,0 +1,262 @@
1
+ import logging
2
+ from typing import Dict, Optional, List
3
+ from pydantic import BaseModel
4
+ from ..agent.agent import Agent
5
+ from ..task.task import Task
6
+
7
+ class LoopItems(BaseModel):
8
+ items: List[any]
9
+
10
+ class Process:
11
+ def __init__(self, tasks: Dict[str, Task], agents: List[Agent], manager_llm: Optional[str] = None, verbose: bool = False):
12
+ self.tasks = tasks
13
+ self.agents = agents
14
+ self.manager_llm = manager_llm
15
+ self.verbose = verbose
16
+
17
+ def workflow(self):
18
+ # Build workflow relationships first
19
+ for task in self.tasks.values():
20
+ if task.next_tasks:
21
+ for next_task_name in task.next_tasks:
22
+ next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
23
+ if next_task:
24
+ next_task.previous_tasks.append(task.name)
25
+
26
+ # Find start task
27
+ start_task = None
28
+ for task_id, task in self.tasks.items():
29
+ if task.is_start:
30
+ start_task = task
31
+ break
32
+
33
+ if not start_task:
34
+ start_task = list(self.tasks.values())[0]
35
+ logging.info("No start task marked, using first task")
36
+
37
+ current_task = start_task
38
+ visited_tasks = set()
39
+ loop_data = {} # Store loop-specific data
40
+
41
+ while current_task and current_task.id not in visited_tasks:
42
+ task_id = current_task.id
43
+ logging.info(f"Executing workflow task: {current_task.name if current_task.name else task_id}")
44
+
45
+ # Add context from previous tasks to description
46
+ if current_task.previous_tasks or current_task.context:
47
+ context = "\nInput data from previous tasks:"
48
+
49
+ # Add data from previous tasks in workflow
50
+ for prev_name in current_task.previous_tasks:
51
+ prev_task = next((t for t in self.tasks.values() if t.name == prev_name), None)
52
+ if prev_task and prev_task.result:
53
+ # Handle loop data
54
+ if current_task.task_type == "loop":
55
+ # create a loop manager Agent
56
+ loop_manager = Agent(
57
+ name="Loop Manager",
58
+ role="Loop data processor",
59
+ goal="Process loop data and convert it to list format",
60
+ backstory="Expert at handling loop data and converting it to proper format",
61
+ llm=self.manager_llm,
62
+ verbose=self.verbose,
63
+ markdown=True
64
+ )
65
+
66
+ # get the loop data convert it to list using calling Agent class chat
67
+ loop_prompt = f"""
68
+ Process this data into a list format:
69
+ {prev_task.result.raw}
70
+
71
+ Return a JSON object with an 'items' array containing the items to process.
72
+ """
73
+ loop_data_str = loop_manager.chat(
74
+ prompt=loop_prompt,
75
+ output_json=LoopItems
76
+ )
77
+
78
+ try:
79
+ # The response will already be parsed into LoopItems model
80
+ loop_data[f"loop_{current_task.name}"] = {
81
+ "items": loop_data_str.items,
82
+ "index": 0,
83
+ "remaining": len(loop_data_str.items)
84
+ }
85
+ context += f"\nCurrent loop item: {loop_data_str.items[0]}"
86
+ except Exception as e:
87
+ display_error(f"Failed to process loop data: {e}")
88
+ context += f"\n{prev_name}: {prev_task.result.raw}"
89
+ else:
90
+ context += f"\n{prev_name}: {prev_task.result.raw}"
91
+
92
+ # Add data from context tasks
93
+ if current_task.context:
94
+ for ctx_task in current_task.context:
95
+ if ctx_task.result and ctx_task.name != current_task.name:
96
+ context += f"\n{ctx_task.name}: {ctx_task.result.raw}"
97
+
98
+ # Update task description with context
99
+ current_task.description = current_task.description + context
100
+
101
+ # Execute task using existing run_task method
102
+ yield task_id
103
+ visited_tasks.add(task_id)
104
+
105
+ # Handle loop progression
106
+ if current_task.task_type == "loop":
107
+ loop_key = f"loop_{current_task.name}"
108
+ if loop_key in loop_data:
109
+ loop_info = loop_data[loop_key]
110
+ loop_info["index"] += 1
111
+ has_more = loop_info["remaining"] > 0
112
+
113
+ # Update result to trigger correct condition
114
+ if current_task.result:
115
+ result = current_task.result.raw
116
+ if has_more:
117
+ result += "\nmore"
118
+ else:
119
+ result += "\ndone"
120
+ current_task.result.raw = result
121
+
122
+ # Determine next task based on result
123
+ next_task = None
124
+ if current_task.result:
125
+ if current_task.task_type in ["decision", "loop"]:
126
+ result = current_task.result.raw.lower()
127
+ # Check conditions
128
+ for condition, tasks in current_task.condition.items():
129
+ if condition.lower() in result and tasks:
130
+ next_task_name = tasks[0]
131
+ next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
132
+ # For loops, allow revisiting the same task
133
+ if next_task and next_task.id == current_task.id:
134
+ visited_tasks.discard(current_task.id)
135
+ break
136
+
137
+ if not next_task and current_task.next_tasks:
138
+ next_task_name = current_task.next_tasks[0]
139
+ next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
140
+
141
+ current_task = next_task
142
+ if not current_task:
143
+ logging.info("Workflow execution completed")
144
+ break
145
+
146
+ def sequential(self):
147
+ for task_id in self.tasks:
148
+ if self.tasks[task_id].status != "completed":
149
+ yield task_id
150
+
151
+ def hierarchical(self):
152
+ logging.debug(f"Starting hierarchical task execution with {len(self.tasks)} tasks")
153
+ manager_agent = Agent(
154
+ name="Manager",
155
+ role="Project manager",
156
+ goal="Manage the entire flow of tasks and delegate them to the right agent",
157
+ backstory="Expert project manager to coordinate tasks among agents",
158
+ llm=self.manager_llm,
159
+ verbose=self.verbose,
160
+ markdown=True,
161
+ self_reflect=False
162
+ )
163
+
164
+ class ManagerInstructions(BaseModel):
165
+ task_id: int
166
+ agent_name: str
167
+ action: str
168
+
169
+ manager_task = Task(
170
+ name="manager_task",
171
+ description="Decide the order of tasks and which agent executes them",
172
+ expected_output="All tasks completed successfully",
173
+ agent=manager_agent
174
+ )
175
+ manager_task_id = yield manager_task
176
+ logging.info(f"Created manager task with ID {manager_task_id}")
177
+
178
+ completed_count = 0
179
+ total_tasks = len(self.tasks) - 1
180
+ logging.info(f"Need to complete {total_tasks} tasks (excluding manager task)")
181
+
182
+ while completed_count < total_tasks:
183
+ tasks_summary = []
184
+ for tid, tk in self.tasks.items():
185
+ if tk.name == "manager_task":
186
+ continue
187
+ task_info = {
188
+ "task_id": tid,
189
+ "name": tk.name,
190
+ "description": tk.description,
191
+ "status": tk.status if tk.status else "not started",
192
+ "agent": tk.agent.name if tk.agent else "No agent"
193
+ }
194
+ tasks_summary.append(task_info)
195
+ logging.info(f"Task {tid} status: {task_info}")
196
+
197
+ manager_prompt = f"""
198
+ Here is the current status of all tasks except yours (manager_task):
199
+ {tasks_summary}
200
+
201
+ Provide a JSON with the structure:
202
+ {{
203
+ "task_id": <int>,
204
+ "agent_name": "<string>",
205
+ "action": "<execute or stop>"
206
+ }}
207
+ """
208
+
209
+ try:
210
+ logging.info("Requesting manager instructions...")
211
+ manager_response = client.beta.chat.completions.parse(
212
+ model=self.manager_llm,
213
+ messages=[
214
+ {"role": "system", "content": manager_task.description},
215
+ {"role": "user", "content": manager_prompt}
216
+ ],
217
+ temperature=0.7,
218
+ response_format=ManagerInstructions
219
+ )
220
+ parsed_instructions = manager_response.choices[0].message.parsed
221
+ logging.info(f"Manager instructions: {parsed_instructions}")
222
+ except Exception as e:
223
+ display_error(f"Manager parse error: {e}")
224
+ logging.error(f"Manager parse error: {str(e)}", exc_info=True)
225
+ break
226
+
227
+ selected_task_id = parsed_instructions.task_id
228
+ selected_agent_name = parsed_instructions.agent_name
229
+ action = parsed_instructions.action
230
+
231
+ logging.info(f"Manager selected task_id={selected_task_id}, agent={selected_agent_name}, action={action}")
232
+
233
+ if action.lower() == "stop":
234
+ logging.info("Manager decided to stop task execution")
235
+ break
236
+
237
+ if selected_task_id not in self.tasks:
238
+ error_msg = f"Manager selected invalid task id {selected_task_id}"
239
+ display_error(error_msg)
240
+ logging.error(error_msg)
241
+ break
242
+
243
+ original_agent = self.tasks[selected_task_id].agent.name if self.tasks[selected_task_id].agent else "None"
244
+ for a in self.agents:
245
+ if a.name == selected_agent_name:
246
+ self.tasks[selected_task_id].agent = a
247
+ logging.info(f"Changed agent for task {selected_task_id} from {original_agent} to {selected_agent_name}")
248
+ break
249
+
250
+ if self.tasks[selected_task_id].status != "completed":
251
+ logging.info(f"Starting execution of task {selected_task_id}")
252
+ yield selected_task_id
253
+ logging.info(f"Finished execution of task {selected_task_id}, status: {self.tasks[selected_task_id].status}")
254
+
255
+ if self.tasks[selected_task_id].status == "completed":
256
+ completed_count += 1
257
+ logging.info(f"Task {selected_task_id} completed. Total completed: {completed_count}/{total_tasks}")
258
+
259
+ self.tasks[manager_task.id].status = "completed"
260
+ if self.verbose >= 1:
261
+ logging.info("All tasks completed under manager supervision.")
262
+ logging.info("Hierarchical task execution finished")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: praisonaiagents
3
- Version: 0.0.14
3
+ Version: 0.0.15
4
4
  Summary: Praison AI agents for completing complex tasks with Self Reflection Agents
5
5
  Author: Mervin Praison
6
6
  Requires-Dist: pydantic
@@ -3,7 +3,7 @@ praisonaiagents/main.py,sha256=K2OxVKPmo4dNJbSWIsXDi_hm9CRx5O4km_74UGcszhk,5744
3
3
  praisonaiagents/agent/__init__.py,sha256=sKO8wGEXvtCrvV1e834r1Okv0XAqAxqZCqz6hKLiTvA,79
4
4
  praisonaiagents/agent/agent.py,sha256=zTYcDpJ5DzzBnefwLvhrtBlGQoRI4ZZAioDu5nKTPSs,24042
5
5
  praisonaiagents/agents/__init__.py,sha256=7RDeQNSqZg5uBjD4M_0p_F6YgfWuDuxPFydPU50kDYc,120
6
- praisonaiagents/agents/agents.py,sha256=ITvH8Yq_OzhyMC_Aid4qlqQbEM9cCfp7SayXg0ASJ5k,24526
6
+ praisonaiagents/agents/agents.py,sha256=ngPFNTmv3whf22litkQacUGaRghX-NbLPAC6Ejy9qoU,13003
7
7
  praisonaiagents/build/lib/praisonaiagents/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
8
8
  praisonaiagents/build/lib/praisonaiagents/main.py,sha256=zDhN5KKtKbfruolDNxlyJkcFlkSt4KQkQTDRfQVAhxc,3960
9
9
  praisonaiagents/build/lib/praisonaiagents/agent/__init__.py,sha256=sKO8wGEXvtCrvV1e834r1Okv0XAqAxqZCqz6hKLiTvA,79
@@ -12,9 +12,11 @@ praisonaiagents/build/lib/praisonaiagents/agents/__init__.py,sha256=cgCLFLFcLp9S
12
12
  praisonaiagents/build/lib/praisonaiagents/agents/agents.py,sha256=P2FAtlfD3kPib5a1oLVYanxlU6e4-GhBMQ0YDY5MHY4,13473
13
13
  praisonaiagents/build/lib/praisonaiagents/task/__init__.py,sha256=VL5hXVmyGjINb34AalxpBMl-YW9m5EDcRkMTKkSSl7c,80
14
14
  praisonaiagents/build/lib/praisonaiagents/task/task.py,sha256=4Y1qX8OeEFcid2yhAiPYylvHpuDmWORsyNL16_BiVvI,1831
15
+ praisonaiagents/process/__init__.py,sha256=lkYbL7Hn5a0ldvJtkdH23vfIIZLIcanK-65C0MwaorY,52
16
+ praisonaiagents/process/process.py,sha256=BgtFgTQjLoqHzj97zDtALjuP_ciOErMDB9quDTlZFjg,11676
15
17
  praisonaiagents/task/__init__.py,sha256=VL5hXVmyGjINb34AalxpBMl-YW9m5EDcRkMTKkSSl7c,80
16
18
  praisonaiagents/task/task.py,sha256=oMC5Zz1dMj0Ceice69aBS1KQQXMLqphc8wNOQ9zcu0Q,2570
17
- praisonaiagents-0.0.14.dist-info/METADATA,sha256=-pdlX7m7Sr2IovIrRt9QyBfkiwgK81rEd3_VklcmHNs,233
18
- praisonaiagents-0.0.14.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
19
- praisonaiagents-0.0.14.dist-info/top_level.txt,sha256=_HsRddrJ23iDx5TTqVUVvXG2HeHBL5voshncAMDGjtA,16
20
- praisonaiagents-0.0.14.dist-info/RECORD,,
19
+ praisonaiagents-0.0.15.dist-info/METADATA,sha256=QERXlBCD9drRfFrI5GwbLzbIaLX_FnOM9UVxFwIwDBo,233
20
+ praisonaiagents-0.0.15.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
21
+ praisonaiagents-0.0.15.dist-info/top_level.txt,sha256=_HsRddrJ23iDx5TTqVUVvXG2HeHBL5voshncAMDGjtA,16
22
+ praisonaiagents-0.0.15.dist-info/RECORD,,