lionagi 0.0.306__py3-none-any.whl → 0.0.308__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.
Files changed (78) hide show
  1. lionagi/__init__.py +2 -5
  2. lionagi/core/__init__.py +7 -5
  3. lionagi/core/agent/__init__.py +3 -0
  4. lionagi/core/agent/base_agent.py +10 -12
  5. lionagi/core/branch/__init__.py +4 -0
  6. lionagi/core/branch/base_branch.py +81 -81
  7. lionagi/core/branch/branch.py +16 -28
  8. lionagi/core/branch/branch_flow_mixin.py +3 -7
  9. lionagi/core/branch/executable_branch.py +86 -56
  10. lionagi/core/branch/util.py +77 -162
  11. lionagi/core/{flow/direct → direct}/__init__.py +1 -1
  12. lionagi/core/{flow/direct/predict.py → direct/parallel_predict.py} +39 -17
  13. lionagi/core/direct/parallel_react.py +0 -0
  14. lionagi/core/direct/parallel_score.py +0 -0
  15. lionagi/core/direct/parallel_select.py +0 -0
  16. lionagi/core/direct/parallel_sentiment.py +0 -0
  17. lionagi/core/direct/predict.py +174 -0
  18. lionagi/core/{flow/direct → direct}/react.py +2 -2
  19. lionagi/core/{flow/direct → direct}/score.py +28 -23
  20. lionagi/core/{flow/direct → direct}/select.py +48 -45
  21. lionagi/core/direct/utils.py +83 -0
  22. lionagi/core/flow/monoflow/ReAct.py +6 -5
  23. lionagi/core/flow/monoflow/__init__.py +9 -0
  24. lionagi/core/flow/monoflow/chat.py +10 -10
  25. lionagi/core/flow/monoflow/chat_mixin.py +11 -10
  26. lionagi/core/flow/monoflow/followup.py +6 -5
  27. lionagi/core/flow/polyflow/__init__.py +1 -0
  28. lionagi/core/flow/polyflow/chat.py +15 -3
  29. lionagi/core/mail/mail_manager.py +18 -19
  30. lionagi/core/mail/schema.py +5 -4
  31. lionagi/core/messages/schema.py +18 -20
  32. lionagi/core/prompt/__init__.py +0 -0
  33. lionagi/core/prompt/prompt_template.py +0 -0
  34. lionagi/core/schema/__init__.py +2 -2
  35. lionagi/core/schema/action_node.py +11 -3
  36. lionagi/core/schema/base_mixin.py +56 -59
  37. lionagi/core/schema/base_node.py +34 -37
  38. lionagi/core/schema/condition.py +24 -0
  39. lionagi/core/schema/data_logger.py +96 -99
  40. lionagi/core/schema/data_node.py +19 -19
  41. lionagi/core/schema/prompt_template.py +0 -0
  42. lionagi/core/schema/structure.py +171 -169
  43. lionagi/core/session/__init__.py +1 -3
  44. lionagi/core/session/session.py +196 -214
  45. lionagi/core/tool/tool_manager.py +95 -103
  46. lionagi/integrations/__init__.py +1 -3
  47. lionagi/integrations/bridge/langchain_/documents.py +17 -18
  48. lionagi/integrations/bridge/langchain_/langchain_bridge.py +14 -14
  49. lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +22 -22
  50. lionagi/integrations/bridge/llamaindex_/node_parser.py +12 -12
  51. lionagi/integrations/bridge/llamaindex_/reader.py +11 -11
  52. lionagi/integrations/bridge/llamaindex_/textnode.py +7 -7
  53. lionagi/integrations/config/openrouter_configs.py +0 -1
  54. lionagi/integrations/provider/oai.py +26 -26
  55. lionagi/integrations/provider/services.py +38 -38
  56. lionagi/libs/__init__.py +34 -1
  57. lionagi/libs/ln_api.py +211 -221
  58. lionagi/libs/ln_async.py +53 -60
  59. lionagi/libs/ln_convert.py +118 -120
  60. lionagi/libs/ln_dataframe.py +32 -33
  61. lionagi/libs/ln_func_call.py +334 -342
  62. lionagi/libs/ln_nested.py +99 -107
  63. lionagi/libs/ln_parse.py +161 -165
  64. lionagi/libs/sys_util.py +52 -52
  65. lionagi/tests/test_core/test_session.py +254 -266
  66. lionagi/tests/test_core/test_session_base_util.py +299 -300
  67. lionagi/tests/test_core/test_tool_manager.py +70 -74
  68. lionagi/tests/test_libs/test_nested.py +2 -7
  69. lionagi/tests/test_libs/test_parse.py +2 -2
  70. lionagi/version.py +1 -1
  71. {lionagi-0.0.306.dist-info → lionagi-0.0.308.dist-info}/METADATA +4 -2
  72. lionagi-0.0.308.dist-info/RECORD +115 -0
  73. lionagi/core/flow/direct/utils.py +0 -43
  74. lionagi-0.0.306.dist-info/RECORD +0 -106
  75. /lionagi/core/{flow/direct → direct}/sentiment.py +0 -0
  76. {lionagi-0.0.306.dist-info → lionagi-0.0.308.dist-info}/LICENSE +0 -0
  77. {lionagi-0.0.306.dist-info → lionagi-0.0.308.dist-info}/WHEEL +0 -0
  78. {lionagi-0.0.306.dist-info → lionagi-0.0.308.dist-info}/top_level.txt +0 -0
lionagi/libs/ln_nested.py CHANGED
@@ -25,13 +25,13 @@ def nset(nested_structure: dict | list, indices: list[int | str], value: Any) ->
25
25
  incorrect.
26
26
 
27
27
  Examples:
28
- >>> data = {'a': {'b': [10, 20]}}
29
- >>> nset(data, ['a', 'b', 1], 99)
30
- >>> assert data == {'a': {'b': [10, 99]}}
28
+ >>> data = {'a': {'b': [10, 20]}}
29
+ >>> nset(data, ['a', 'b', 1], 99)
30
+ >>> assert data == {'a': {'b': [10, 99]}}
31
31
 
32
- >>> data = [0, [1, 2], 3]
33
- >>> nset(data, [1, 1], 99)
34
- >>> assert data == [0, [1, 99], 3]
32
+ >>> data = [0, [1, 2], 3]
33
+ >>> nset(data, [1, 1], 99)
34
+ >>> assert data == [0, [1, 99], 3]
35
35
  """
36
36
  if not indices:
37
37
  raise ValueError("Indices list is empty, cannot determine target container")
@@ -77,12 +77,12 @@ def nget(
77
77
  and no default value is provided.
78
78
 
79
79
  Examples:
80
- >>> data = {'a': {'b': [10, 20]}}
81
- >>> assert nget(data, ['a', 'b', 1]) == 20
82
- >>> nget(data, ['a', 'b', 2])
83
- Traceback (most recent call last):
84
- ...
85
- LookupError: Target not found and no default value provided.
80
+ >>> data = {'a': {'b': [10, 20]}}
81
+ >>> assert nget(data, ['a', 'b', 1]) == 20
82
+ >>> nget(data, ['a', 'b', 2])
83
+ Traceback (most recent call last):
84
+ ...
85
+ LookupError: Target not found and no default value provided.
86
86
  """
87
87
 
88
88
  try:
@@ -140,20 +140,20 @@ def nmerge(
140
140
  that defines custom sorting logic for the merged list.
141
141
 
142
142
  Returns:
143
- A merged dictionary or list,
144
- depending on the types present in `iterables`.
143
+ A merged dictionary or list,
144
+ depending on the types present in `iterables`.
145
145
 
146
146
  Raises:
147
- TypeError:
148
- If `iterables`
149
- contains objects of incompatible types that cannot be merged.
147
+ TypeError:
148
+ If `iterables`
149
+ contains objects of incompatible types that cannot be merged.
150
150
 
151
151
  examples:
152
- >>> nmerge([{'a': 1}, {'b': 2}], overwrite=True)
153
- {'a': 1, 'b': 2}
152
+ >>> nmerge([{'a': 1}, {'b': 2}], overwrite=True)
153
+ {'a': 1, 'b': 2}
154
154
 
155
- >>> nmerge([[1, 2], [3, 4]], sort_list=True)
156
- [1, 2, 3, 4]
155
+ >>> nmerge([[1, 2], [3, 4]], sort_list=True)
156
+ [1, 2, 3, 4]
157
157
  """
158
158
  if convert.is_homogeneous(nested_structure, dict):
159
159
  return _merge_dicts(
@@ -196,19 +196,19 @@ def flatten(
196
196
  True, only flattens nested dictionaries, leaving lists intact.
197
197
 
198
198
  Returns:
199
- A flattened dictionary, or None if `inplace` is True.
199
+ A flattened dictionary, or None if `inplace` is True.
200
200
 
201
201
  Raises:
202
- ValueError: If `inplace` is True but `nested_structure` is not a dictionary.
202
+ ValueError: If `inplace` is True but `nested_structure` is not a dictionary.
203
203
 
204
204
  examples:
205
- >>> nested_dict = {'a': {'b': {'c': 1}}}
206
- >>> flatten(nested_dict)
207
- {'a_b_c': 1}
205
+ >>> nested_dict = {'a': {'b': {'c': 1}}}
206
+ >>> flatten(nested_dict)
207
+ {'a_b_c': 1}
208
208
 
209
- >>> nested_list = [{'a': 1}, {'b': 2}]
210
- >>> flatten(nested_list)
211
- {'0_a': 1, '1_b': 2}
209
+ >>> nested_list = [{'a': 1}, {'b': 2}]
210
+ >>> flatten(nested_list)
211
+ {'0_a': 1, '1_b': 2}
212
212
  """
213
213
  if inplace:
214
214
  if not isinstance(nested_structure, dict):
@@ -250,22 +250,22 @@ def unflatten(
250
250
  keys and can limit the reconstruction depth.
251
251
 
252
252
  Args:
253
- flat_dict: A flat dictionary with composite keys to unflatten.
254
- sep: The sep used in composite keys, indicating nested levels.
255
- custom_logic: An optional function to process each part of the composite keys.
256
- max_depth: The maximum depth for nesting during reconstruction.
253
+ flat_dict: A flat dictionary with composite keys to unflatten.
254
+ sep: The sep used in composite keys, indicating nested levels.
255
+ custom_logic: An optional function to process each part of the composite keys.
256
+ max_depth: The maximum depth for nesting during reconstruction.
257
257
 
258
258
  Returns:
259
- The reconstructed nested dictionary or list.
259
+ The reconstructed nested dictionary or list.
260
260
 
261
261
  examples:
262
- >>> flat_dict_ = {'a_b_c': 1}
263
- >>> unflatten(flat_dict_)
264
- {'a': {'b': {'c': 1}}}
262
+ >>> flat_dict_ = {'a_b_c': 1}
263
+ >>> unflatten(flat_dict_)
264
+ {'a': {'b': {'c': 1}}}
265
265
 
266
- >>> flat_dict_ = {'0_a': 1, '1_b': 2}
267
- >>> unflatten(flat_dict_)
268
- [{'a': 1}, {'b': 2}]
266
+ >>> flat_dict_ = {'0_a': 1, '1_b': 2}
267
+ >>> unflatten(flat_dict_)
268
+ [{'a': 1}, {'b': 2}]
269
269
  """
270
270
  unflattened = {}
271
271
  for composite_key, value in flat_dict.items():
@@ -285,9 +285,7 @@ def unflatten(
285
285
  ):
286
286
  max_index = max(unflattened.keys(), default=-1)
287
287
  return [unflattened.get(i) for i in range(max_index + 1)]
288
- if not unflattened:
289
- return {}
290
- return unflattened
288
+ return unflattened or {}
291
289
 
292
290
 
293
291
  def nfilter(
@@ -310,13 +308,13 @@ def nfilter(
310
308
  containing only items that meet the condition.
311
309
 
312
310
  Raises:
313
- TypeError: Raised if `collection` is not a dictionary or a list.
311
+ TypeError: Raised if `collection` is not a dictionary or a list.
314
312
 
315
313
  Examples:
316
- >>> nfilter({'a': 1, 'b': 2, 'c': 3}, lambda x: x[1] > 1)
317
- {'b': 2, 'c': 3}
318
- >>> nfilter([1, 2, 3, 4], lambda x: x % 2 == 0)
319
- [2, 4]
314
+ >>> nfilter({'a': 1, 'b': 2, 'c': 3}, lambda x: x[1] > 1)
315
+ {'b': 2, 'c': 3}
316
+ >>> nfilter([1, 2, 3, 4], lambda x: x % 2 == 0)
317
+ [2, 4]
320
318
  """
321
319
  if isinstance(nested_structure, dict):
322
320
  return _filter_dict(nested_structure, condition)
@@ -353,13 +351,13 @@ def ninsert(
353
351
  depth during recursive calls.
354
352
 
355
353
  Examples:
356
- >>> subject_ = {'a': {'b': [1, 2]}}
357
- >>> ninsert(subject_, ['a', 'b', 2], 3)
358
- >>> assert subject_ == {'a': {'b': [1, 2, 3]}}
354
+ >>> subject_ = {'a': {'b': [1, 2]}}
355
+ >>> ninsert(subject_, ['a', 'b', 2], 3)
356
+ >>> assert subject_ == {'a': {'b': [1, 2, 3]}}
359
357
 
360
- >>> subject_ = []
361
- >>> ninsert(subject_, [0, 'a'], 1)
362
- >>> assert subject_ == [{'a': 1}]
358
+ >>> subject_ = []
359
+ >>> ninsert(subject_, [0, 'a'], 1)
360
+ >>> assert subject_ == [{'a': 1}]
363
361
  """
364
362
  indices = convert.to_list(indices)
365
363
  parts_len = len(indices)
@@ -376,12 +374,10 @@ def ninsert(
376
374
  ):
377
375
  next_part = indices[i + 1]
378
376
  nested_structure[part] = [] if isinstance(next_part, int) else {}
379
- nested_structure = nested_structure[part]
380
- else:
381
- if part not in nested_structure:
382
- next_part = indices[i + 1]
383
- nested_structure[part] = [] if isinstance(next_part, int) else {}
384
- nested_structure = nested_structure[part]
377
+ elif part not in nested_structure:
378
+ next_part = indices[i + 1]
379
+ nested_structure[part] = [] if isinstance(next_part, int) else {}
380
+ nested_structure = nested_structure[part]
385
381
  current_depth += 1
386
382
  parts_depth += 1
387
383
 
@@ -391,11 +387,10 @@ def ninsert(
391
387
  last_part = indices[-1]
392
388
  if isinstance(last_part, int):
393
389
  _handle_list_insert(nested_structure, last_part, value)
390
+ elif isinstance(nested_structure, list):
391
+ nested_structure.append({last_part: value})
394
392
  else:
395
- if isinstance(nested_structure, list):
396
- nested_structure.append({last_part: value})
397
- else:
398
- nested_structure[last_part] = value
393
+ nested_structure[last_part] = value
399
394
 
400
395
 
401
396
  # noinspection PyDecorator
@@ -423,32 +418,29 @@ def get_flattened_keys(
423
418
  modifying the original object.
424
419
 
425
420
  Returns:
426
- A list of strings representing the keys in the flattened structure.
421
+ A list of strings representing the keys in the flattened structure.
427
422
 
428
423
  Raises:
429
- ValueError: If `inplace` is True but `nested_structure` is not a dictionary.
424
+ ValueError: If `inplace` is True but `nested_structure` is not a dictionary.
430
425
 
431
426
  Examples:
432
- >>> nested_dict = {'a': 1, 'b': {'c': 2, 'd': {'e': 3}}}
433
- >>> keys = get_flattened_keys(nested_dict)
434
- >>> assert keys == ['a', 'b_c', 'b_d_e']
427
+ >>> nested_dict = {'a': 1, 'b': {'c': 2, 'd': {'e': 3}}}
428
+ >>> keys = get_flattened_keys(nested_dict)
429
+ >>> assert keys == ['a', 'b_c', 'b_d_e']
435
430
 
436
- >>> nested_list = [{'a': 1}, {'b': 2}]
437
- >>> keys = get_flattened_keys(nested_list)
438
- >>> assert keys == ['0_a', '1_b']
431
+ >>> nested_list = [{'a': 1}, {'b': 2}]
432
+ >>> keys = get_flattened_keys(nested_list)
433
+ >>> assert keys == ['0_a', '1_b']
439
434
  """
440
- if inplace:
441
- obj_copy = SysUtil.create_copy(nested_structure, num=1)
442
- flatten(
443
- obj_copy, sep=sep, max_depth=max_depth, inplace=True, dict_only=dict_only
444
- )
445
- return convert.to_list(obj_copy.keys())
446
- else:
435
+ if not inplace:
447
436
  return convert.to_list(
448
437
  flatten(
449
438
  nested_structure, sep=sep, max_depth=max_depth, dict_only=dict_only
450
439
  ).keys()
451
440
  )
441
+ obj_copy = SysUtil.create_copy(nested_structure, num=1)
442
+ flatten(obj_copy, sep=sep, max_depth=max_depth, inplace=True, dict_only=dict_only)
443
+ return convert.to_list(obj_copy.keys())
452
444
 
453
445
 
454
446
  def _dynamic_flatten_in_place(
@@ -469,19 +461,19 @@ def _dynamic_flatten_in_place(
469
461
  dictionaries and to a certain depth.
470
462
 
471
463
  Args:
472
- nested_structure: The structure to flatten.
473
- parent_key: Initial key prefix for all keys in the flattened structure.
474
- sep: Separator for nested keys.
475
- max_depth: Limits the flattening to a specific depth.
476
- current_depth: Tracks the current depth in the recursion.
477
- dict_only: Limits the flattening to dictionaries only, ignoring lists.
464
+ nested_structure: The structure to flatten.
465
+ parent_key: Initial key prefix for all keys in the flattened structure.
466
+ sep: Separator for nested keys.
467
+ max_depth: Limits the flattening to a specific depth.
468
+ current_depth: Tracks the current depth in the recursion.
469
+ dict_only: Limits the flattening to dictionaries only, ignoring lists.
478
470
 
479
471
  Note:
480
- This function modifies `nested_structure` in place.
472
+ This function modifies `nested_structure` in place.
481
473
 
482
474
  Examples:
483
- Given a nested dictionary `nested_dict` with the appropriate structure,
484
- `_dynamic_flatten_in_place(nested_dict)` will modify it to a flattened form.
475
+ Given a nested dictionary `nested_dict` with the appropriate structure,
476
+ `_dynamic_flatten_in_place(nested_dict)` will modify it to a flattened form.
485
477
  """
486
478
  if isinstance(nested_structure, dict):
487
479
  keys_to_delete = []
@@ -525,12 +517,12 @@ def _handle_list_insert(nested_structure: list, part: int, value: Any) -> None:
525
517
  `None` values up to the index, then the specified value is inserted.
526
518
 
527
519
  Args:
528
- nested_structure: The list to modify.
529
- part: The target index for inserting or replacing the value.
530
- value: The value to be inserted or to replace an existing value in the list.
520
+ nested_structure: The list to modify.
521
+ part: The target index for inserting or replacing the value.
522
+ value: The value to be inserted or to replace an existing value in the list.
531
523
 
532
524
  Note:
533
- This function directly modifies the input list in place.
525
+ This function directly modifies the input list in place.
534
526
  """
535
527
  while len(nested_structure) <= part:
536
528
  nested_structure.append(None)
@@ -547,9 +539,9 @@ def _ensure_list_index(lst_: list, index: int, default: Any = None) -> None:
547
539
  with a specified default value until it reaches the required length.
548
540
 
549
541
  Args:
550
- lst_: The list to extend.
551
- index: The target index that the list should reach or exceed.
552
- default: The value to append to the list for extension. Defaults to None.
542
+ lst_: The list to extend.
543
+ index: The target index that the list should reach or exceed.
544
+ default: The value to append to the list for extension. Defaults to None.
553
545
 
554
546
  Note: Modifies the list in place, ensuring it can safely be indexed at `index`
555
547
  without raising an IndexError.
@@ -569,14 +561,14 @@ def _deep_update(original: dict, update: dict) -> dict:
569
561
  the key-value pair to `original`.
570
562
 
571
563
  Args:
572
- original: The dictionary to update.
573
- update: The dictionary containing updates to apply to `original`.
564
+ original: The dictionary to update.
565
+ update: The dictionary containing updates to apply to `original`.
574
566
 
575
567
  Returns:
576
- The `original` dictionary after applying updates from `update`.
568
+ The `original` dictionary after applying updates from `update`.
577
569
 
578
570
  Note:
579
- This method modifies the `original` dictionary in place.
571
+ This method modifies the `original` dictionary in place.
580
572
  """
581
573
  for key, value in update.items():
582
574
  if isinstance(value, dict) and key in original:
@@ -646,14 +638,14 @@ def _deep_merge_dicts(dict1: dict, dict2: dict) -> dict:
646
638
  pairs from `dict2`.
647
639
 
648
640
  Args:
649
- dict1: The target dictionary to update with values from `dict2`.
650
- dict2: The source dictionary providing updates and additional key-value pairs.
641
+ dict1: The target dictionary to update with values from `dict2`.
642
+ dict2: The source dictionary providing updates and additional key-value pairs.
651
643
 
652
644
  Returns:
653
- The updated dictionary `dict1` with deeply merged values from `dict2`.
645
+ The updated dictionary `dict1` with deeply merged values from `dict2`.
654
646
 
655
647
  Note:
656
- Modifies `dict1` in place, reflecting merged changes from `dict2`.
648
+ Modifies `dict1` in place, reflecting merged changes from `dict2`.
657
649
  """
658
650
  for key in dict2:
659
651
  if key in dict1:
@@ -712,9 +704,9 @@ def _merge_sequences(
712
704
  mechanism or a custom sorting function provided by the user.
713
705
 
714
706
  Args:
715
- iterables: A collection of iterable sequences to be merged.
716
- sort_list: Determines whether to sort the merged list.
717
- custom_sort: Optional. A function defining custom sort criteria.
707
+ iterables: A collection of iterable sequences to be merged.
708
+ sort_list: Determines whether to sort the merged list.
709
+ custom_sort: Optional. A function defining custom sort criteria.
718
710
 
719
711
  Returns: list[Any]: The merged (and potentially sorted) list of elements from
720
712
  all provided iterables.
@@ -765,7 +757,7 @@ def _filter_list(lst: list[Any], condition: Callable[[Any], bool]) -> list[Any]:
765
757
  the filtered list.
766
758
 
767
759
  Returns:
768
- list[Any]: A new list comprising elements that meet the condition.
760
+ list[Any]: A new list comprising elements that meet the condition.
769
761
  """
770
762
  return [item for item in lst if condition(item)]
771
763