lionagi 0.15.1__py3-none-any.whl → 0.15.3__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.
@@ -253,9 +253,11 @@ class OperationGraphBuilder:
253
253
  if not sources:
254
254
  raise ValueError("No source nodes for aggregation")
255
255
 
256
- # Add aggregation metadata
256
+ # Add aggregation metadata - convert IDType to strings for JSON serialization
257
257
  agg_params = {
258
- "aggregation_sources": sources,
258
+ "aggregation_sources": [
259
+ str(s) for s in sources
260
+ ], # Convert IDType to strings
259
261
  "aggregation_count": len(sources),
260
262
  **parameters,
261
263
  }
@@ -58,6 +58,7 @@ class DependencyAwareExecutor:
58
58
  self.results = {}
59
59
  self.completion_events = {} # operation_id -> Event
60
60
  self.operation_branches = {} # operation_id -> Branch
61
+ self.skipped_operations = set() # Track skipped operations
61
62
 
62
63
  # Initialize completion events for all operations
63
64
  # and check for already completed operations
@@ -76,6 +77,9 @@ class DependencyAwareExecutor:
76
77
  if not self.graph.is_acyclic():
77
78
  raise ValueError("Graph must be acyclic for flow execution")
78
79
 
80
+ # Validate edge conditions before execution
81
+ self._validate_edge_conditions()
82
+
79
83
  # Pre-allocate ALL branches upfront to avoid any locking during execution
80
84
  await self._preallocate_all_branches()
81
85
 
@@ -94,13 +98,25 @@ class DependencyAwareExecutor:
94
98
  if isinstance(node, Operation):
95
99
  await tg.start_soon(self._execute_operation, node, limiter)
96
100
 
97
- # Return results
98
- return {
99
- "completed_operations": list(self.results.keys()),
101
+ # Return results - only include actually completed operations
102
+ completed_ops = [
103
+ op_id
104
+ for op_id in self.results.keys()
105
+ if op_id not in self.skipped_operations
106
+ ]
107
+
108
+ result = {
109
+ "completed_operations": completed_ops,
100
110
  "operation_results": self.results,
101
111
  "final_context": self.context,
112
+ "skipped_operations": list(self.skipped_operations),
102
113
  }
103
114
 
115
+ # Validate results before returning
116
+ self._validate_execution_results(result)
117
+
118
+ return result
119
+
104
120
  async def _preallocate_all_branches(self):
105
121
  """Pre-allocate ALL branches including for context inheritance to eliminate runtime locking."""
106
122
  operations_needing_branches = []
@@ -193,6 +209,23 @@ class DependencyAwareExecutor:
193
209
  return
194
210
 
195
211
  try:
212
+ # Check if this operation should be skipped due to edge conditions
213
+ should_execute = await self._check_edge_conditions(operation)
214
+
215
+ if not should_execute:
216
+ # Mark as skipped
217
+ operation.execution.status = EventStatus.SKIPPED
218
+ self.skipped_operations.add(operation.id)
219
+
220
+ if self.verbose:
221
+ print(
222
+ f"Skipping operation due to edge conditions: {str(operation.id)[:8]}"
223
+ )
224
+
225
+ # Signal completion so dependent operations can proceed
226
+ self.completion_events[operation.id].set()
227
+ return
228
+
196
229
  # Wait for dependencies
197
230
  await self._wait_for_dependencies(operation)
198
231
 
@@ -235,9 +268,56 @@ class DependencyAwareExecutor:
235
268
  print(f"Operation {str(operation.id)[:8]} failed: {e}")
236
269
 
237
270
  finally:
238
- # Signal completion regardless of success/failure
271
+ # Signal completion regardless of success/failure/skip
239
272
  self.completion_events[operation.id].set()
240
273
 
274
+ async def _check_edge_conditions(self, operation: Operation) -> bool:
275
+ """
276
+ Check if operation should execute based on edge conditions.
277
+
278
+ Returns True if at least one valid path exists to this operation,
279
+ or if there are no incoming edges (head nodes).
280
+ Returns False if all incoming edges have failed conditions.
281
+ """
282
+ # Get all incoming edges
283
+ incoming_edges = [
284
+ edge
285
+ for edge in self.graph.internal_edges.values()
286
+ if edge.tail == operation.id
287
+ ]
288
+
289
+ # If no incoming edges, this is a head node - always execute
290
+ if not incoming_edges:
291
+ return True
292
+
293
+ # Check each incoming edge
294
+ has_valid_path = False
295
+
296
+ for edge in incoming_edges:
297
+ # Wait for the head operation to complete first
298
+ if edge.head in self.completion_events:
299
+ await self.completion_events[edge.head].wait()
300
+
301
+ # Check if the head operation was skipped
302
+ if edge.head in self.skipped_operations:
303
+ continue # This path is not valid
304
+
305
+ # Build context for edge condition evaluation
306
+ result_value = self.results.get(edge.head)
307
+ if result_value is not None and not isinstance(
308
+ result_value, (str, int, float, bool)
309
+ ):
310
+ result_value = to_dict(result_value, recursive=True)
311
+
312
+ ctx = {"result": result_value, "context": self.context}
313
+
314
+ # Use edge.check_condition() which handles None conditions
315
+ if await edge.check_condition(ctx):
316
+ has_valid_path = True
317
+ break # At least one valid path found
318
+
319
+ return has_valid_path
320
+
241
321
  async def _wait_for_dependencies(self, operation: Operation):
242
322
  """Wait for all dependencies to complete."""
243
323
  # Special handling for aggregations
@@ -248,10 +328,14 @@ class DependencyAwareExecutor:
248
328
  f"Aggregation {str(operation.id)[:8]} waiting for {len(sources)} sources"
249
329
  )
250
330
 
251
- # Wait for ALL sources
252
- for source_id in sources:
253
- if source_id in self.completion_events:
254
- await self.completion_events[source_id].wait()
331
+ # Wait for ALL sources (sources are now strings from builder.py)
332
+ for source_id_str in sources:
333
+ # Convert string back to IDType for lookup
334
+ # Check all operations to find matching ID
335
+ for op_id in self.completion_events.keys():
336
+ if str(op_id) == source_id_str:
337
+ await self.completion_events[op_id].wait()
338
+ break
255
339
 
256
340
  # Regular dependency checking
257
341
  predecessors = self.graph.get_predecessors(operation)
@@ -262,32 +346,6 @@ class DependencyAwareExecutor:
262
346
  )
263
347
  await self.completion_events[pred.id].wait()
264
348
 
265
- # Check edge conditions
266
- incoming_edges = [
267
- edge
268
- for edge in self.graph.internal_edges.values()
269
- if edge.tail == operation.id
270
- ]
271
-
272
- for edge in incoming_edges:
273
- # Wait for head to complete
274
- if edge.head in self.completion_events:
275
- await self.completion_events[edge.head].wait()
276
-
277
- # Evaluate edge condition
278
- if edge.condition is not None:
279
- result_value = self.results.get(edge.head)
280
- if result_value is not None and not isinstance(
281
- result_value, (str, int, float, bool)
282
- ):
283
- result_value = to_dict(result_value, recursive=True)
284
-
285
- ctx = {"result": result_value, "context": self.context}
286
- if not await edge.condition.apply(ctx):
287
- raise ValueError(
288
- f"Edge condition not satisfied for {str(operation.id)[:8]}"
289
- )
290
-
291
349
  def _prepare_operation(self, operation: Operation):
292
350
  """Prepare operation with context and branch assignment."""
293
351
  # Update operation context with predecessors
@@ -295,13 +353,18 @@ class DependencyAwareExecutor:
295
353
  if predecessors:
296
354
  pred_context = {}
297
355
  for pred in predecessors:
356
+ # Skip if predecessor was skipped
357
+ if pred.id in self.skipped_operations:
358
+ continue
359
+
298
360
  if pred.id in self.results:
299
361
  result = self.results[pred.id]
300
362
  if result is not None and not isinstance(
301
363
  result, (str, int, float, bool)
302
364
  ):
303
365
  result = to_dict(result, recursive=True)
304
- pred_context[f"{pred.id}_result"] = result
366
+ # Use string representation of IDType for JSON serialization
367
+ pred_context[f"{str(pred.id)}_result"] = result
305
368
 
306
369
  if "context" not in operation.parameters:
307
370
  operation.parameters["context"] = pred_context
@@ -364,7 +427,12 @@ class DependencyAwareExecutor:
364
427
  ):
365
428
  branch._message_manager.pile.clear()
366
429
  for msg in primary_branch._message_manager.pile:
367
- branch._message_manager.pile.append(msg.clone())
430
+ if hasattr(msg, "clone"):
431
+ branch._message_manager.pile.append(
432
+ msg.clone()
433
+ )
434
+ else:
435
+ branch._message_manager.pile.append(msg)
368
436
 
369
437
  # Clear the pending flag
370
438
  branch.metadata["pending_context_inheritance"] = False
@@ -386,6 +454,49 @@ class DependencyAwareExecutor:
386
454
  return self._default_branch
387
455
  return self.session.default_branch
388
456
 
457
+ def _validate_edge_conditions(self):
458
+ """Validate that all edge conditions are properly configured."""
459
+ for edge in self.graph.internal_edges.values():
460
+ if edge.condition is not None:
461
+ # Ensure condition is an EdgeCondition instance
462
+ from lionagi.protocols.graph.edge import EdgeCondition
463
+
464
+ if not isinstance(edge.condition, EdgeCondition):
465
+ raise TypeError(
466
+ f"Edge {edge.id} has invalid condition type: {type(edge.condition)}. "
467
+ "Must be EdgeCondition or None."
468
+ )
469
+
470
+ # Ensure condition has apply method
471
+ if not hasattr(edge.condition, "apply"):
472
+ raise AttributeError(
473
+ f"Edge {edge.id} condition missing 'apply' method."
474
+ )
475
+
476
+ def _validate_execution_results(self, results: dict[str, Any]):
477
+ """Validate execution results for consistency."""
478
+ completed = set(results.get("completed_operations", []))
479
+ skipped = set(results.get("skipped_operations", []))
480
+
481
+ # Check for operations in both lists
482
+ overlap = completed & skipped
483
+ if overlap:
484
+ raise RuntimeError(
485
+ f"Operations {overlap} appear in both completed and skipped lists! "
486
+ "This indicates a bug in edge condition handling."
487
+ )
488
+
489
+ # Verify skipped operations have proper status
490
+ for node in self.graph.internal_nodes.values():
491
+ if isinstance(node, Operation) and node.id in skipped:
492
+ if node.execution.status != EventStatus.SKIPPED:
493
+ # Log warning but don't fail - status might not be perfectly synced
494
+ if self.verbose:
495
+ print(
496
+ f"Warning: Skipped operation {node.id} has status "
497
+ f"{node.execution.status} instead of SKIPPED"
498
+ )
499
+
389
500
 
390
501
  async def flow(
391
502
  session: Session,
@@ -3,6 +3,7 @@ import logging
3
3
  from typing import Any, Literal
4
4
  from uuid import UUID
5
5
 
6
+ from anyio import get_cancelled_exc_class
6
7
  from pydantic import BaseModel, Field
7
8
 
8
9
  from lionagi.protocols.types import ID, Event, EventStatus, IDType, Node
@@ -87,9 +88,9 @@ class Operation(Node, Event):
87
88
  self.execution.response = response
88
89
  self.execution.status = EventStatus.COMPLETED
89
90
 
90
- except asyncio.CancelledError:
91
+ except get_cancelled_exc_class():
91
92
  self.execution.error = "Operation cancelled"
92
- self.execution.status = EventStatus.FAILED
93
+ self.execution.status = EventStatus.CANCELLED
93
94
  raise
94
95
 
95
96
  except Exception as e:
@@ -32,12 +32,14 @@ class EventStatus(str, Enum):
32
32
  PROCESSING: Action is currently being executed.
33
33
  COMPLETED: Action completed successfully.
34
34
  FAILED: Action failed during execution.
35
+ SKIPPED: Action was skipped due to unmet conditions.
35
36
  """
36
37
 
37
38
  PENDING = "pending"
38
39
  PROCESSING = "processing"
39
40
  COMPLETED = "completed"
40
41
  FAILED = "failed"
42
+ SKIPPED = "skipped"
41
43
  CANCELLED = "cancelled"
42
44
  ABORTED = "aborted"
43
45
 
@@ -197,7 +197,7 @@ class APICalling(Event):
197
197
 
198
198
  except get_cancelled_exc_class():
199
199
  self.execution.error = "API call cancelled"
200
- self.execution.status = EventStatus.FAILED
200
+ self.execution.status = EventStatus.CANCELLED
201
201
  raise
202
202
 
203
203
  except Exception as e:
@@ -148,6 +148,8 @@ class Endpoint:
148
148
  "chat_model",
149
149
  "imodel",
150
150
  "branch",
151
+ "aggregation_sources",
152
+ "aggregation_count",
151
153
  }
152
154
  payload = {
153
155
  k: v for k, v in payload.items() if k not in non_api_params
@@ -86,6 +86,31 @@ class Session(Node, Communicatable, Relational):
86
86
  ):
87
87
  self._operation_manager.register(operation, func, update=update)
88
88
 
89
+ def operation(self, name: str = None, *, update: bool = False):
90
+ """
91
+ Decorator to automatically register functions as operations.
92
+
93
+ Args:
94
+ name: Operation name. If None, uses the function's __name__.
95
+ update: Whether to update if operation already exists.
96
+
97
+ Usage:
98
+ @session.operation()
99
+ async def read_issue():
100
+ ...
101
+
102
+ @session.operation("custom_name")
103
+ async def some_function():
104
+ ...
105
+ """
106
+
107
+ def decorator(func: Callable) -> Callable:
108
+ operation_name = name if name is not None else func.__name__
109
+ self.register_operation(operation_name, func, update=update)
110
+ return func
111
+
112
+ return decorator
113
+
89
114
  @model_validator(mode="after")
90
115
  def _add_mail_sources(self) -> Self:
91
116
  if self.default_branch is None:
lionagi/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.15.1"
1
+ __version__ = "0.15.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lionagi
3
- Version: 0.15.1
3
+ Version: 0.15.3
4
4
  Summary: An Intelligence Operating System.
5
5
  Author-email: HaiyangLi <quantocean.li@gmail.com>
6
6
  License: Apache License
@@ -497,7 +497,7 @@ We welcome issues, ideas, and pull requests:
497
497
 
498
498
  ```
499
499
  @software{Li_LionAGI_2023,
500
- author = {Haiyang Li, Liangbingyan Luo},
500
+ author = {Haiyang Li},
501
501
  month = {12},
502
502
  year = {2023},
503
503
  title = {LionAGI: Towards Automated General Intelligence},
@@ -6,7 +6,7 @@ lionagi/config.py,sha256=W3JOC_TFad8hFkpTG8yv0-GNupa7x3wX4NAUfWpB59U,3763
6
6
  lionagi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  lionagi/settings.py,sha256=HDuKCEJCpc4HudKodBnhoQUGuTGhRHdlIFhbtf3VBtY,1633
8
8
  lionagi/utils.py,sha256=Adtr1wyrU9Ra-HfHDoHLWasD6V88Z8sqkg2CQ8i8nzI,38686
9
- lionagi/version.py,sha256=PQVflpJdJDETGf2g1BB5OHCS_5KtgteERschfoSBtss,23
9
+ lionagi/version.py,sha256=WbcBdqaNCRHsRqs6rGd3pRLP4kQSII2td9dR0Fl6HcU,23
10
10
  lionagi/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  lionagi/adapters/async_postgres_adapter.py,sha256=Kf2YCzwRqKpEHY3GQCXEiMl201CCIkDvXcvddwZNkkE,12723
12
12
  lionagi/adapters/postgres_model_adapter.py,sha256=e_wfJNyihbpLCXuAs_W9tbLoMXAXbAXtkQDaHfqWz3o,4555
@@ -108,10 +108,10 @@ lionagi/models/note.py,sha256=okWJL4mGqt0bUVxHRyqsfJr7tEh6wwgYhF1CegxudIA,12202
108
108
  lionagi/models/operable_model.py,sha256=Zm_Hxdauqyh0za3_TJLCZ3g6nR4F45Rrnc0ZM3d54Gs,20111
109
109
  lionagi/models/schema_model.py,sha256=ghRIM8aBNaToAknwNlhQKpuKXcwzyCw5pDE31bVKxs0,667
110
110
  lionagi/operations/__init__.py,sha256=L22n8rRls9oVdzHoDlznHFGiKJMNw3ZhbwVQbm1Fn6M,584
111
- lionagi/operations/builder.py,sha256=_fIW5OqPTTond1VUapMMtEwsWmIuTAKy34K24uuK6EQ,22790
112
- lionagi/operations/flow.py,sha256=PNEIGVVTKoXVPbtNcoNNO72-NgtGCx1xMsL7JATeH0M,17561
111
+ lionagi/operations/builder.py,sha256=TDUaNoMbFyK2yIVSlcVh1IREUfewsfNnKSle6__oq4M,22918
112
+ lionagi/operations/flow.py,sha256=fktzGdhRx7XLrFPM2fe9mIpKBog_qeUzf4wHuLEswEo,22228
113
113
  lionagi/operations/manager.py,sha256=YZr3VjPAZVVFd_bIjF1aoQqzzKZHNA1kcqefNi5QFFM,683
114
- lionagi/operations/node.py,sha256=1cKcuAWwbuR5VJqF9oh9o898wO31ky0ev69m3UmnBAI,3135
114
+ lionagi/operations/node.py,sha256=qmjhv-8UzQMO5ocBlNWuv9nqQiLh5CV7AW_tno8jIUM,3183
115
115
  lionagi/operations/types.py,sha256=fM8HphnbBifMzhoKKvdl3JxGCBHlEGPJEYkLWj9b7vE,704
116
116
  lionagi/operations/utils.py,sha256=b8HmpF5vRgmf6PTzg3_fZCIKMnhoGa0HCyOlX6JCz7g,1263
117
117
  lionagi/operations/ReAct/ReAct.py,sha256=UzW3MClv9_w1AqrZIuATOZ9ASTb8HPD0Rlotlp0mYOg,13388
@@ -156,7 +156,7 @@ lionagi/protocols/forms/form.py,sha256=B4903km_Ljz-OxYkb1ZT_cpHZSaAYYJpZMsffWloo
156
156
  lionagi/protocols/forms/report.py,sha256=SvJJjOSCTfVuqK7AKaY8ldQIGJeSK2zoyPWUV41ge2c,1609
157
157
  lionagi/protocols/generic/__init__.py,sha256=5y5joOZzfFWERl75auAcNcKC3lImVJ5ZZGvvHZUFCJM,112
158
158
  lionagi/protocols/generic/element.py,sha256=Eaij2YpTWsGk28Tqjazmjmc_tOnalH7_iGFZrL6QJb4,14420
159
- lionagi/protocols/generic/event.py,sha256=hoMWMnis2Ih6xbkBGVydP_TzfDurcJUDovkdZjEseN0,6517
159
+ lionagi/protocols/generic/event.py,sha256=cAkj6hiStPPJNlaYpmIXEgnix3LVAYYyDDjoubuT0ks,6602
160
160
  lionagi/protocols/generic/log.py,sha256=Y06zAQewkNlaIWjut_c6c45KY_LJfLHwzUaDGLULaas,8212
161
161
  lionagi/protocols/generic/pile.py,sha256=rzhKytyizb5xoyBOeAtDwvLZQhGR04NkgLyr3O_XzS8,30418
162
162
  lionagi/protocols/generic/processor.py,sha256=c_a7HB9WAaCY-HoI19YyStef8WOXcDj9UeiQb5bz_TM,11759
@@ -198,8 +198,8 @@ lionagi/service/resilience.py,sha256=uYJYZQ9M-tje8ME3vJmYabXwKHF1c3Ij4-WrdCwogcs
198
198
  lionagi/service/token_calculator.py,sha256=piTidArzUkIMCtOLC_HBLoZNYZcENQywgeKM31bxezM,6457
199
199
  lionagi/service/types.py,sha256=9zX08BhdmPE9FE1YTyvsy14hdDGRYzyyVBmoBURzQvI,1096
200
200
  lionagi/service/connections/__init__.py,sha256=yHQZ7OJpCftd6CStYR8inbxjJydYdmv9kCvbUBhJ2zU,362
201
- lionagi/service/connections/api_calling.py,sha256=FX9PbpZr3TypgJYfh3G7-gOpaYHLP54GLidvJbbTvAg,9955
202
- lionagi/service/connections/endpoint.py,sha256=yNIjq9wETMnytynGbq3qY_dkyaMlaHrcfiZjS-tnmLg,14756
201
+ lionagi/service/connections/api_calling.py,sha256=fY-fzwSJvQKpUT27TF0MTfE5TxroYKkL4SHWYrmYznI,9958
202
+ lionagi/service/connections/endpoint.py,sha256=UYjryqKF4uEaGzDP9vYrav-0Xr6tDR6J9FM_kwqR87Q,14832
203
203
  lionagi/service/connections/endpoint_config.py,sha256=6sA06uCzriT6p0kFxhDCFH8N6V6MVp8ytlOw5ctBhDI,5169
204
204
  lionagi/service/connections/header_factory.py,sha256=IYeTQQk7r8FXcdhmW7orCxHjNO-Nb1EOXhgNK7CAp-I,1821
205
205
  lionagi/service/connections/match_endpoint.py,sha256=kHAs6FmWJWjIn4CO7FwZVi8w1lYcvu2vGSf9ww2sCG4,2470
@@ -227,14 +227,14 @@ lionagi/service/third_party/pplx_models.py,sha256=-EhyJgOWR6rzSv3zczUtk80X6c19p1
227
227
  lionagi/session/__init__.py,sha256=kDypY6L3kGPnatAw7YNQAykgg-9MlIBnlhHExaXvt-c,202
228
228
  lionagi/session/branch.py,sha256=yfOtMITbtKzGAcxwZ5JjkXrKtb2PBGRPt53xqbR0lAs,68329
229
229
  lionagi/session/prompts.py,sha256=GPr0jibyAAqS3awDzGC8SoCL6aWJLLCCbXY0JUuxOC0,3170
230
- lionagi/session/session.py,sha256=C66yzISZh7r6reAIOOCg3JxQuSKYDHos7b_skNRpO2s,12481
230
+ lionagi/session/session.py,sha256=FzsUsqEQ6cnGd57E4HmEucftz5nMKsfj9kemRDqsXwU,13257
231
231
  lionagi/tools/__init__.py,sha256=5y5joOZzfFWERl75auAcNcKC3lImVJ5ZZGvvHZUFCJM,112
232
232
  lionagi/tools/base.py,sha256=hEGnE4MD0CM4UqnF0xsDRKB0aM-pyrTFHl8utHhyJLU,1897
233
233
  lionagi/tools/types.py,sha256=XtJLY0m-Yi_ZLWhm0KycayvqMCZd--HxfQ0x9vFUYDE,230
234
234
  lionagi/tools/file/__init__.py,sha256=5y5joOZzfFWERl75auAcNcKC3lImVJ5ZZGvvHZUFCJM,112
235
235
  lionagi/tools/file/reader.py,sha256=jnSHVSQ66AHZXQrgRuGmlbwKT5JHYoo-1zv1hKgTEfc,9544
236
236
  lionagi/tools/memory/tools.py,sha256=earYkKxSOz_iXkqVZYTEDfE3dwZYIWPXZrqQ1DYGz4I,15941
237
- lionagi-0.15.1.dist-info/METADATA,sha256=f7nShb1IWGDEwY_PmbpvJWfLnmKCClalfhKy4fSYljw,22913
238
- lionagi-0.15.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
239
- lionagi-0.15.1.dist-info/licenses/LICENSE,sha256=VXFWsdoN5AAknBCgFqQNgPWYx7OPp-PFEP961zGdOjc,11288
240
- lionagi-0.15.1.dist-info/RECORD,,
237
+ lionagi-0.15.3.dist-info/METADATA,sha256=vGts1USZhbyGGcLRd2eF7t2iDpzDl9gDLoVh_aEnrqs,22895
238
+ lionagi-0.15.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
239
+ lionagi-0.15.3.dist-info/licenses/LICENSE,sha256=VXFWsdoN5AAknBCgFqQNgPWYx7OPp-PFEP961zGdOjc,11288
240
+ lionagi-0.15.3.dist-info/RECORD,,