praisonaiagents 0.0.14__py3-none-any.whl → 0.0.15__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.
@@ -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,,