lionagi 0.2.11__py3-none-any.whl → 0.3.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. lionagi/core/action/function_calling.py +13 -6
  2. lionagi/core/action/tool.py +10 -9
  3. lionagi/core/action/tool_manager.py +18 -9
  4. lionagi/core/agent/README.md +1 -1
  5. lionagi/core/agent/base_agent.py +5 -2
  6. lionagi/core/agent/eval/README.md +1 -1
  7. lionagi/core/collections/README.md +1 -1
  8. lionagi/core/collections/_logger.py +16 -6
  9. lionagi/core/collections/abc/README.md +1 -1
  10. lionagi/core/collections/abc/component.py +35 -11
  11. lionagi/core/collections/abc/concepts.py +5 -3
  12. lionagi/core/collections/abc/exceptions.py +3 -1
  13. lionagi/core/collections/flow.py +16 -5
  14. lionagi/core/collections/model.py +34 -8
  15. lionagi/core/collections/pile.py +65 -28
  16. lionagi/core/collections/progression.py +1 -2
  17. lionagi/core/collections/util.py +11 -2
  18. lionagi/core/director/README.md +1 -1
  19. lionagi/core/engine/branch_engine.py +35 -10
  20. lionagi/core/engine/instruction_map_engine.py +14 -5
  21. lionagi/core/engine/sandbox_.py +3 -1
  22. lionagi/core/engine/script_engine.py +6 -2
  23. lionagi/core/executor/base_executor.py +10 -3
  24. lionagi/core/executor/graph_executor.py +12 -4
  25. lionagi/core/executor/neo4j_executor.py +18 -6
  26. lionagi/core/generic/edge.py +7 -2
  27. lionagi/core/generic/graph.py +23 -7
  28. lionagi/core/generic/node.py +14 -5
  29. lionagi/core/generic/tree_node.py +5 -1
  30. lionagi/core/mail/mail_manager.py +3 -1
  31. lionagi/core/mail/package.py +3 -1
  32. lionagi/core/message/action_request.py +9 -2
  33. lionagi/core/message/action_response.py +9 -3
  34. lionagi/core/message/instruction.py +8 -2
  35. lionagi/core/message/util.py +15 -5
  36. lionagi/core/report/base.py +12 -7
  37. lionagi/core/report/form.py +7 -4
  38. lionagi/core/report/report.py +10 -3
  39. lionagi/core/report/util.py +3 -1
  40. lionagi/core/rule/action.py +4 -1
  41. lionagi/core/rule/base.py +17 -6
  42. lionagi/core/rule/rulebook.py +8 -4
  43. lionagi/core/rule/string.py +3 -1
  44. lionagi/core/session/branch.py +15 -4
  45. lionagi/core/session/session.py +6 -2
  46. lionagi/core/unit/parallel_unit.py +9 -3
  47. lionagi/core/unit/template/action.py +1 -1
  48. lionagi/core/unit/template/predict.py +3 -1
  49. lionagi/core/unit/template/select.py +5 -3
  50. lionagi/core/unit/unit.py +4 -2
  51. lionagi/core/unit/unit_form.py +13 -15
  52. lionagi/core/unit/unit_mixin.py +45 -27
  53. lionagi/core/unit/util.py +7 -3
  54. lionagi/core/validator/validator.py +28 -15
  55. lionagi/core/work/work_edge.py +7 -3
  56. lionagi/core/work/work_task.py +11 -5
  57. lionagi/core/work/worker.py +20 -5
  58. lionagi/core/work/worker_engine.py +6 -2
  59. lionagi/core/work/worklog.py +3 -1
  60. lionagi/experimental/compressor/llm_compressor.py +20 -5
  61. lionagi/experimental/directive/README.md +1 -1
  62. lionagi/experimental/directive/parser/base_parser.py +41 -14
  63. lionagi/experimental/directive/parser/base_syntax.txt +23 -23
  64. lionagi/experimental/directive/template/base_template.py +14 -6
  65. lionagi/experimental/directive/tokenizer.py +3 -1
  66. lionagi/experimental/evaluator/README.md +1 -1
  67. lionagi/experimental/evaluator/ast_evaluator.py +6 -2
  68. lionagi/experimental/evaluator/base_evaluator.py +27 -16
  69. lionagi/integrations/bridge/autogen_/autogen_.py +7 -3
  70. lionagi/integrations/bridge/langchain_/documents.py +13 -10
  71. lionagi/integrations/bridge/llamaindex_/llama_pack.py +36 -12
  72. lionagi/integrations/bridge/llamaindex_/node_parser.py +8 -3
  73. lionagi/integrations/bridge/llamaindex_/reader.py +3 -1
  74. lionagi/integrations/bridge/llamaindex_/textnode.py +9 -3
  75. lionagi/integrations/bridge/pydantic_/pydantic_bridge.py +7 -1
  76. lionagi/integrations/bridge/transformers_/install_.py +3 -1
  77. lionagi/integrations/chunker/chunk.py +5 -2
  78. lionagi/integrations/loader/load.py +7 -3
  79. lionagi/integrations/loader/load_util.py +35 -16
  80. lionagi/integrations/provider/oai.py +13 -4
  81. lionagi/integrations/provider/openrouter.py +13 -4
  82. lionagi/integrations/provider/services.py +3 -1
  83. lionagi/integrations/provider/transformers.py +5 -3
  84. lionagi/integrations/storage/neo4j.py +23 -7
  85. lionagi/integrations/storage/storage_util.py +23 -7
  86. lionagi/integrations/storage/structure_excel.py +7 -2
  87. lionagi/integrations/storage/to_csv.py +8 -2
  88. lionagi/integrations/storage/to_excel.py +11 -3
  89. lionagi/libs/ln_api.py +41 -19
  90. lionagi/libs/ln_context.py +4 -4
  91. lionagi/libs/ln_convert.py +35 -14
  92. lionagi/libs/ln_dataframe.py +9 -3
  93. lionagi/libs/ln_func_call.py +53 -18
  94. lionagi/libs/ln_image.py +9 -5
  95. lionagi/libs/ln_knowledge_graph.py +21 -7
  96. lionagi/libs/ln_nested.py +57 -16
  97. lionagi/libs/ln_parse.py +45 -15
  98. lionagi/libs/ln_queue.py +8 -3
  99. lionagi/libs/ln_tokenize.py +19 -6
  100. lionagi/libs/ln_validate.py +14 -3
  101. lionagi/libs/sys_util.py +44 -12
  102. lionagi/lions/coder/coder.py +24 -8
  103. lionagi/lions/coder/util.py +6 -2
  104. lionagi/lions/researcher/data_source/google_.py +12 -4
  105. lionagi/lions/researcher/data_source/wiki_.py +3 -1
  106. lionagi/version.py +1 -1
  107. {lionagi-0.2.11.dist-info → lionagi-0.3.0.dist-info}/METADATA +6 -7
  108. lionagi-0.3.0.dist-info/RECORD +226 -0
  109. lionagi/tests/__init__.py +0 -0
  110. lionagi/tests/api/__init__.py +0 -0
  111. lionagi/tests/api/aws/__init__.py +0 -0
  112. lionagi/tests/api/aws/conftest.py +0 -25
  113. lionagi/tests/api/aws/test_aws_s3.py +0 -6
  114. lionagi/tests/integrations/__init__.py +0 -0
  115. lionagi/tests/libs/__init__.py +0 -0
  116. lionagi/tests/libs/test_api.py +0 -48
  117. lionagi/tests/libs/test_convert.py +0 -89
  118. lionagi/tests/libs/test_field_validators.py +0 -354
  119. lionagi/tests/libs/test_func_call.py +0 -701
  120. lionagi/tests/libs/test_nested.py +0 -382
  121. lionagi/tests/libs/test_parse.py +0 -171
  122. lionagi/tests/libs/test_queue.py +0 -68
  123. lionagi/tests/libs/test_sys_util.py +0 -222
  124. lionagi/tests/test_core/__init__.py +0 -0
  125. lionagi/tests/test_core/collections/__init__.py +0 -0
  126. lionagi/tests/test_core/collections/test_component.py +0 -208
  127. lionagi/tests/test_core/collections/test_exchange.py +0 -139
  128. lionagi/tests/test_core/collections/test_flow.py +0 -146
  129. lionagi/tests/test_core/collections/test_pile.py +0 -172
  130. lionagi/tests/test_core/collections/test_progression.py +0 -130
  131. lionagi/tests/test_core/generic/__init__.py +0 -0
  132. lionagi/tests/test_core/generic/test_edge.py +0 -69
  133. lionagi/tests/test_core/generic/test_graph.py +0 -97
  134. lionagi/tests/test_core/generic/test_node.py +0 -107
  135. lionagi/tests/test_core/generic/test_structure.py +0 -194
  136. lionagi/tests/test_core/generic/test_tree_node.py +0 -74
  137. lionagi/tests/test_core/graph/__init__.py +0 -0
  138. lionagi/tests/test_core/graph/test_graph.py +0 -71
  139. lionagi/tests/test_core/graph/test_tree.py +0 -76
  140. lionagi/tests/test_core/mail/__init__.py +0 -0
  141. lionagi/tests/test_core/mail/test_mail.py +0 -98
  142. lionagi/tests/test_core/test_branch.py +0 -116
  143. lionagi/tests/test_core/test_form.py +0 -47
  144. lionagi/tests/test_core/test_report.py +0 -106
  145. lionagi/tests/test_core/test_structure/__init__.py +0 -0
  146. lionagi/tests/test_core/test_structure/test_base_structure.py +0 -198
  147. lionagi/tests/test_core/test_structure/test_graph.py +0 -55
  148. lionagi/tests/test_core/test_structure/test_tree.py +0 -49
  149. lionagi/tests/test_core/test_validator.py +0 -112
  150. lionagi-0.2.11.dist-info/RECORD +0 -267
  151. {lionagi-0.2.11.dist-info → lionagi-0.3.0.dist-info}/LICENSE +0 -0
  152. {lionagi-0.2.11.dist-info → lionagi-0.3.0.dist-info}/WHEEL +0 -0
lionagi/libs/ln_nested.py CHANGED
@@ -1,12 +1,15 @@
1
1
  from collections import defaultdict
2
+ from collections.abc import Callable, Generator
2
3
  from itertools import chain
3
- from typing import Any, Callable, Generator
4
+ from typing import Any
4
5
 
5
6
  import lionagi.libs.ln_convert as convert
6
7
  from lionagi.libs.sys_util import SysUtil
7
8
 
8
9
 
9
- def nset(nested_structure: dict | list, indices: list[int | str], value: Any) -> None:
10
+ def nset(
11
+ nested_structure: dict | list, indices: list[int | str], value: Any
12
+ ) -> None:
10
13
  """
11
14
  sets a value within a nested structure at the specified path defined by indices.
12
15
 
@@ -34,7 +37,9 @@ def nset(nested_structure: dict | list, indices: list[int | str], value: Any) ->
34
37
  >>> assert data == [0, [1, 99], 3]
35
38
  """
36
39
  if not indices:
37
- raise ValueError("Indices list is empty, cannot determine target container")
40
+ raise ValueError(
41
+ "Indices list is empty, cannot determine target container"
42
+ )
38
43
 
39
44
  _indices = convert.to_list(indices)
40
45
  target_container = _get_target_container(nested_structure, _indices[:-1])
@@ -86,7 +91,9 @@ def nget(
86
91
  """
87
92
 
88
93
  try:
89
- target_container = _get_target_container(nested_structure, indices[:-1])
94
+ target_container = _get_target_container(
95
+ nested_structure, indices[:-1]
96
+ )
90
97
  last_index = indices[-1]
91
98
 
92
99
  if (
@@ -96,17 +103,24 @@ def nget(
96
103
  ):
97
104
 
98
105
  return target_container[last_index]
99
- elif isinstance(target_container, dict) and last_index in target_container:
106
+ elif (
107
+ isinstance(target_container, dict)
108
+ and last_index in target_container
109
+ ):
100
110
  return target_container[last_index]
101
111
  elif default is not ...:
102
112
  return default
103
113
  else:
104
- raise LookupError("Target not found and no default value provided.")
114
+ raise LookupError(
115
+ "Target not found and no default value provided."
116
+ )
105
117
  except (IndexError, KeyError, TypeError):
106
118
  if default is not ...:
107
119
  return default
108
120
  else:
109
- raise LookupError("Target not found and no default value provided.")
121
+ raise LookupError(
122
+ "Target not found and no default value provided."
123
+ )
110
124
 
111
125
 
112
126
  # nested merge
@@ -212,7 +226,9 @@ def flatten(
212
226
  """
213
227
  if inplace:
214
228
  if not isinstance(nested_structure, dict):
215
- raise ValueError("Object must be a dictionary when 'inplace' is True.")
229
+ raise ValueError(
230
+ "Object must be a dictionary when 'inplace' is True."
231
+ )
216
232
  _dynamic_flatten_in_place(
217
233
  nested_structure,
218
234
  parent_key=parent_key,
@@ -278,7 +294,13 @@ def unflatten(
278
294
  if not unflattened and all(isinstance(part, int) for part in parts):
279
295
  unflattened = []
280
296
 
281
- ninsert(unflattened, indices=parts, value=value, sep=sep, max_depth=max_depth)
297
+ ninsert(
298
+ unflattened,
299
+ indices=parts,
300
+ value=value,
301
+ sep=sep,
302
+ max_depth=max_depth,
303
+ )
282
304
 
283
305
  if isinstance(unflattened, dict) and all(
284
306
  isinstance(k, int) for k in unflattened.keys()
@@ -321,7 +343,9 @@ def nfilter(
321
343
  elif isinstance(nested_structure, list):
322
344
  return _filter_list(nested_structure, condition)
323
345
  else:
324
- raise TypeError("The nested_structure must be either a dict or a list.")
346
+ raise TypeError(
347
+ "The nested_structure must be either a dict or a list."
348
+ )
325
349
 
326
350
 
327
351
  def ninsert(
@@ -373,7 +397,9 @@ def ninsert(
373
397
  nested_structure[part], (dict, list)
374
398
  ):
375
399
  next_part = indices[i + 1]
376
- nested_structure[part] = [] if isinstance(next_part, int) else {}
400
+ nested_structure[part] = (
401
+ [] if isinstance(next_part, int) else {}
402
+ )
377
403
  elif part not in nested_structure:
378
404
  next_part = indices[i + 1]
379
405
  nested_structure[part] = [] if isinstance(next_part, int) else {}
@@ -434,11 +460,20 @@ def get_flattened_keys(
434
460
  if not inplace:
435
461
  return convert.to_list(
436
462
  flatten(
437
- nested_structure, sep=sep, max_depth=max_depth, dict_only=dict_only
463
+ nested_structure,
464
+ sep=sep,
465
+ max_depth=max_depth,
466
+ dict_only=dict_only,
438
467
  ).keys()
439
468
  )
440
469
  obj_copy = SysUtil.create_copy(nested_structure, num=1)
441
- flatten(obj_copy, sep=sep, max_depth=max_depth, inplace=True, dict_only=dict_only)
470
+ flatten(
471
+ obj_copy,
472
+ sep=sep,
473
+ max_depth=max_depth,
474
+ inplace=True,
475
+ dict_only=dict_only,
476
+ )
442
477
  return convert.to_list(obj_copy.keys())
443
478
 
444
479
 
@@ -484,7 +519,9 @@ def _dynamic_flatten_in_place(
484
519
  for k, v in items:
485
520
  new_key = f"{parent_key}{sep}{k}" if parent_key else k
486
521
 
487
- if isinstance(v, dict) and (max_depth is None or current_depth < max_depth):
522
+ if isinstance(v, dict) and (
523
+ max_depth is None or current_depth < max_depth
524
+ ):
488
525
  _dynamic_flatten_in_place(
489
526
  v,
490
527
  parent_key=new_key,
@@ -744,7 +781,9 @@ def _filter_dict(
744
781
  return {k: v for k, v in dictionary.items() if condition((k, v))}
745
782
 
746
783
 
747
- def _filter_list(lst: list[Any], condition: Callable[[Any], bool]) -> list[Any]:
784
+ def _filter_list(
785
+ lst: list[Any], condition: Callable[[Any], bool]
786
+ ) -> list[Any]:
748
787
  """
749
788
  Filters elements in a list based on a specified condition.
750
789
 
@@ -777,5 +816,7 @@ def _get_target_container(
777
816
  else:
778
817
  raise KeyError("Key not found in dictionary")
779
818
  else:
780
- raise TypeError("Current element is neither a list nor a dictionary")
819
+ raise TypeError(
820
+ "Current element is neither a list nor a dictionary"
821
+ )
781
822
  return current_element
lionagi/libs/ln_parse.py CHANGED
@@ -63,7 +63,9 @@ class ParseUtil:
63
63
  open_brackets.append(brackets[char])
64
64
  elif char in brackets.values():
65
65
  if not open_brackets or open_brackets[-1] != char:
66
- raise ValueError("Mismatched or extra closing bracket found.")
66
+ raise ValueError(
67
+ "Mismatched or extra closing bracket found."
68
+ )
67
69
  open_brackets.pop()
68
70
 
69
71
  return str_to_parse + "".join(reversed(open_brackets))
@@ -94,7 +96,9 @@ class ParseUtil:
94
96
  def replacement(match):
95
97
  char = match.group(0)
96
98
  _char_map = char_map or md_json_char_map
97
- return _char_map.get(char, char) # Default to the char itself if not in map
99
+ return _char_map.get(
100
+ char, char
101
+ ) # Default to the char itself if not in map
98
102
 
99
103
  # Match any of the special characters to be escaped.
100
104
  return re.sub(r'[\n\r\t"]', replacement, value)
@@ -149,7 +153,9 @@ class ParseUtil:
149
153
  )
150
154
  if not match:
151
155
  str_to_parse = str_to_parse.strip()
152
- if str_to_parse.startswith("```json\n") and str_to_parse.endswith("\n```"):
156
+ if str_to_parse.startswith("```json\n") and str_to_parse.endswith(
157
+ "\n```"
158
+ ):
153
159
  str_to_parse = str_to_parse[8:-4].strip()
154
160
 
155
161
  parser = parser or ParseUtil.fuzzy_parse_json
@@ -209,11 +215,15 @@ class ParseUtil:
209
215
  {'key': 'value'}
210
216
  """
211
217
  json_obj = ParseUtil.extract_json_block(
212
- str_to_parse, language="json", parser=parser or ParseUtil.fuzzy_parse_json
218
+ str_to_parse,
219
+ language="json",
220
+ parser=parser or ParseUtil.fuzzy_parse_json,
213
221
  )
214
222
 
215
223
  if expected_keys:
216
- if missing_keys := [key for key in expected_keys if key not in json_obj]:
224
+ if missing_keys := [
225
+ key for key in expected_keys if key not in json_obj
226
+ ]:
217
227
  raise ValueError(
218
228
  f"Missing expected keys in JSON object: {', '.join(missing_keys)}"
219
229
  )
@@ -276,7 +286,9 @@ class ParseUtil:
276
286
  elif lines[i].startswith(" "):
277
287
  param_desc = lines[i].split(":", 1)
278
288
  if len(param_desc) == 1:
279
- params_description[current_param] += f" {param_desc[0].strip()}"
289
+ params_description[
290
+ current_param
291
+ ] += f" {param_desc[0].strip()}"
280
292
  continue
281
293
  param, desc = param_desc
282
294
  param = param.split("(")[0].strip()
@@ -443,9 +455,13 @@ class ParseUtil:
443
455
  func_name = func.__name__
444
456
 
445
457
  if not func_description:
446
- func_description, _ = ParseUtil._extract_docstring_details(func, style)
458
+ func_description, _ = ParseUtil._extract_docstring_details(
459
+ func, style
460
+ )
447
461
  if not params_description:
448
- _, params_description = ParseUtil._extract_docstring_details(func, style)
462
+ _, params_description = ParseUtil._extract_docstring_details(
463
+ func, style
464
+ )
449
465
 
450
466
  # Extracting parameters with typing hints
451
467
  sig = inspect.signature(func)
@@ -459,7 +475,9 @@ class ParseUtil:
459
475
  # Default type to string and update if type hint is available
460
476
  param_type = "string"
461
477
  if param.annotation is not inspect.Parameter.empty:
462
- param_type = ParseUtil._python_to_json_type(param.annotation.__name__)
478
+ param_type = ParseUtil._python_to_json_type(
479
+ param.annotation.__name__
480
+ )
463
481
 
464
482
  # Extract parameter description from docstring, if available
465
483
  param_description = params_description.get(
@@ -546,7 +564,9 @@ class StringMatch:
546
564
 
547
565
  transpositions //= 2
548
566
  return (
549
- matches / s_len + matches / t_len + (matches - transpositions) / matches
567
+ matches / s_len
568
+ + matches / t_len
569
+ + (matches - transpositions) / matches
550
570
  ) / 3.0
551
571
 
552
572
  @staticmethod
@@ -639,7 +659,9 @@ class StringMatch:
639
659
  used_keys.add(k)
640
660
  else:
641
661
  # Calculate Jaro-Winkler similarity scores for each potential match
642
- scores = np.array([score_func(k, field) for field in fields_set])
662
+ scores = np.array(
663
+ [score_func(k, field) for field in fields_set]
664
+ )
643
665
  # Find the index of the highest score
644
666
  max_score_index = np.argmax(scores)
645
667
  # Select the best match based on the highest score
@@ -697,19 +719,25 @@ class StringMatch:
697
719
 
698
720
  except Exception:
699
721
  # if still failed we try to extract the json block using re and parse it again
700
- match = re.search(r"```json\n({.*?})\n```", out_, re.DOTALL)
722
+ match = re.search(
723
+ r"```json\n({.*?})\n```", out_, re.DOTALL
724
+ )
701
725
  if match:
702
726
  out_ = match.group(1)
703
727
  try:
704
728
  out_ = ParseUtil.fuzzy_parse_json(out_)
705
- return StringMatch.correct_dict_keys(keys, out_)
729
+ return StringMatch.correct_dict_keys(
730
+ keys, out_
731
+ )
706
732
 
707
733
  except:
708
734
  try:
709
735
  out_ = ParseUtil.fuzzy_parse_json(
710
736
  out_.replace("'", '"')
711
737
  )
712
- return StringMatch.correct_dict_keys(keys, out_)
738
+ return StringMatch.correct_dict_keys(
739
+ keys, out_
740
+ )
713
741
  except:
714
742
  pass
715
743
 
@@ -717,4 +745,6 @@ class StringMatch:
717
745
  try:
718
746
  return StringMatch.correct_dict_keys(keys, out_)
719
747
  except Exception as e:
720
- raise ValueError(f"Failed to force_validate_dict for input: {x}") from e
748
+ raise ValueError(
749
+ f"Failed to force_validate_dict for input: {x}"
750
+ ) from e
lionagi/libs/ln_queue.py CHANGED
@@ -3,7 +3,8 @@ A class that manages asynchronous task processing with controlled concurrency.
3
3
  """
4
4
 
5
5
  import asyncio
6
- from typing import Any, Callable
6
+ from collections.abc import Callable
7
+ from typing import Any
7
8
 
8
9
  from lionagi.libs import func_call
9
10
 
@@ -69,7 +70,9 @@ class AsyncQueue:
69
70
  """
70
71
  return self._stop_event.is_set()
71
72
 
72
- async def process_requests(self, func: Callable, retry_kwargs: dict = {}) -> None:
73
+ async def process_requests(
74
+ self, func: Callable, retry_kwargs: dict = {}
75
+ ) -> None:
73
76
  """
74
77
  Processes tasks from the queue using the provided function with retries.
75
78
 
@@ -85,7 +88,9 @@ class AsyncQueue:
85
88
  tasks = set()
86
89
  while not self.stopped():
87
90
  if len(tasks) >= self.max_concurrent_tasks:
88
- _, done = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
91
+ _, done = await asyncio.wait(
92
+ tasks, return_when=asyncio.FIRST_COMPLETED
93
+ )
89
94
  tasks.difference_update(done)
90
95
 
91
96
  async with self.semaphore:
@@ -20,11 +20,16 @@ class TokenizeUtil:
20
20
 
21
21
  if encoding_model:
22
22
  try:
23
- encoding_name = tiktoken.encoding_name_for_model(encoding_model)
23
+ encoding_name = tiktoken.encoding_name_for_model(
24
+ encoding_model
25
+ )
24
26
  except:
25
27
  encoding_name = encoding_name or "cl100k_base"
26
28
 
27
- if not encoding_name or encoding_name in tiktoken.list_encoding_names():
29
+ if (
30
+ not encoding_name
31
+ or encoding_name in tiktoken.list_encoding_names()
32
+ ):
28
33
  encoding_name = encoding_name or "cl100k_base"
29
34
  encoding = tiktoken.get_encoding(encoding_name)
30
35
 
@@ -88,9 +93,13 @@ class TokenizeUtil:
88
93
  chunks.append(text[start_idx:end_idx])
89
94
 
90
95
  if len(text) - chunk_size * (n_chunks - 1) > threshold:
91
- chunks.append(text[chunk_size * (n_chunks - 1) - overlap_size :])
96
+ chunks.append(
97
+ text[chunk_size * (n_chunks - 1) - overlap_size :]
98
+ )
92
99
  else:
93
- chunks[-1] += text[chunk_size * (n_chunks - 1) + overlap_size :]
100
+ chunks[-1] += text[
101
+ chunk_size * (n_chunks - 1) + overlap_size :
102
+ ]
94
103
 
95
104
  return chunks
96
105
 
@@ -157,10 +166,14 @@ class TokenizeUtil:
157
166
  chunks.append(tokens[start_idx:end_idx])
158
167
 
159
168
  if len(tokens) - chunk_size * (n_chunks - 1) > threshold:
160
- chunks.append(tokens[chunk_size * (n_chunks - 1) - overlap_size :])
169
+ chunks.append(
170
+ tokens[chunk_size * (n_chunks - 1) - overlap_size :]
171
+ )
161
172
  else:
162
173
  chunks[-1] += tokens[-residue:]
163
174
 
164
175
  return (
165
- [" ".join(chunk) for chunk in chunks] if not return_tokens else chunks
176
+ [" ".join(chunk) for chunk in chunks]
177
+ if not return_tokens
178
+ else chunks
166
179
  )
@@ -6,7 +6,14 @@ including numeric, boolean, string, and enum. It also provides a dictionary `val
6
6
  maps data types to their corresponding validation functions.
7
7
  """
8
8
 
9
- from .ln_convert import is_same_dtype, strip_lower, to_dict, to_list, to_num, to_str
9
+ from .ln_convert import (
10
+ is_same_dtype,
11
+ strip_lower,
12
+ to_dict,
13
+ to_list,
14
+ to_num,
15
+ to_str,
16
+ )
10
17
  from .ln_parse import ParseUtil, StringMatch
11
18
 
12
19
 
@@ -19,7 +26,9 @@ def check_dict_field(x, keys: list[str] | dict, fix_=True, **kwargs):
19
26
  return StringMatch.force_validate_dict(x, keys=keys, **kwargs)
20
27
  except Exception as e:
21
28
  raise ValueError("Invalid dict field type.") from e
22
- raise ValueError(f"Default value for DICT must be a dict, got {type(x).__name__}")
29
+ raise ValueError(
30
+ f"Default value for DICT must be a dict, got {type(x).__name__}"
31
+ )
23
32
 
24
33
 
25
34
  def check_action_field(x, fix_=True, **kwargs):
@@ -275,7 +284,9 @@ def _fix_enum_field(x, choices, **kwargs):
275
284
  x = to_str(x)
276
285
  return StringMatch.choose_most_similar(x, choices, **kwargs)
277
286
  except Exception as e:
278
- raise ValueError(f"Failed to convert {x} into one of the choices") from e
287
+ raise ValueError(
288
+ f"Failed to convert {x} into one of the choices"
289
+ ) from e
279
290
 
280
291
 
281
292
  validation_funcs = {
lionagi/libs/sys_util.py CHANGED
@@ -48,7 +48,9 @@ class SysUtil:
48
48
  return datetime.now(**config_)
49
49
 
50
50
  @staticmethod
51
- def change_dict_key(dict_: dict[Any, Any], old_key: str, new_key: str) -> None:
51
+ def change_dict_key(
52
+ dict_: dict[Any, Any], old_key: str, new_key: str
53
+ ) -> None:
52
54
  """Safely changes a key in a dictionary if the old key exists.
53
55
 
54
56
  Args:
@@ -122,7 +124,9 @@ class SysUtil:
122
124
  return sha256(current_time + random_bytes).hexdigest()[:n]
123
125
 
124
126
  @staticmethod
125
- def get_bins(input_: list[str], upper: int | None = 2000) -> list[list[int]]:
127
+ def get_bins(
128
+ input_: list[str], upper: int | None = 2000
129
+ ) -> list[list[int]]:
126
130
  """Organizes indices of strings into bins based on a cumulative upper limit.
127
131
 
128
132
  Args:
@@ -157,7 +161,11 @@ class SysUtil:
157
161
  str: A string identifying the CPU architecture ('apple_silicon' or 'other_cpu').
158
162
  """
159
163
  arch: str = platform.machine().lower()
160
- return "apple_silicon" if "arm" in arch or "aarch64" in arch else "other_cpu"
164
+ return (
165
+ "apple_silicon"
166
+ if "arm" in arch or "aarch64" in arch
167
+ else "other_cpu"
168
+ )
161
169
 
162
170
  @staticmethod
163
171
  def install_import(
@@ -195,7 +203,9 @@ class SysUtil:
195
203
  print(
196
204
  f"Module {full_import_path} or attribute {import_name} not found. Installing {pip_name}..."
197
205
  )
198
- subprocess.check_call([sys.executable, "-m", "pip", "install", pip_name])
206
+ subprocess.check_call(
207
+ [sys.executable, "-m", "pip", "install", pip_name]
208
+ )
199
209
 
200
210
  # Retry the import after installation
201
211
  if import_name:
@@ -254,18 +264,25 @@ class SysUtil:
254
264
  package_name, module_name, import_name, pip_name
255
265
  )
256
266
  else:
257
- logging.info(f"Package {package_name} not found. {error_message}")
267
+ logging.info(
268
+ f"Package {package_name} not found. {error_message}"
269
+ )
258
270
  raise ImportError(
259
271
  f"Package {package_name} not found. {error_message}"
260
272
  )
261
273
  except ImportError as e: # More specific exception handling
262
274
  logging.error(f"Failed to import {package_name}. Error: {e}")
263
- raise ValueError(f"Failed to import {package_name}. Error: {e}") from e
275
+ raise ValueError(
276
+ f"Failed to import {package_name}. Error: {e}"
277
+ ) from e
264
278
 
265
279
  @staticmethod
266
280
  def list_installed_packages() -> list:
267
281
  """list all installed packages using importlib.metadata."""
268
- return [dist.metadata["Name"] for dist in importlib.metadata.distributions()]
282
+ return [
283
+ dist.metadata["Name"]
284
+ for dist in importlib.metadata.distributions()
285
+ ]
269
286
 
270
287
  @staticmethod
271
288
  def uninstall_package(package_name: str) -> None:
@@ -283,7 +300,14 @@ class SysUtil:
283
300
  """Update a specified package."""
284
301
  try:
285
302
  subprocess.check_call(
286
- [sys.executable, "-m", "pip", "install", "--upgrade", package_name]
303
+ [
304
+ sys.executable,
305
+ "-m",
306
+ "pip",
307
+ "install",
308
+ "--upgrade",
309
+ package_name,
310
+ ]
287
311
  )
288
312
  print(f"Successfully updated {package_name}.")
289
313
  except subprocess.CalledProcessError as e:
@@ -291,7 +315,9 @@ class SysUtil:
291
315
 
292
316
  @staticmethod
293
317
  def clear_dir(
294
- dir_path: Path | str, recursive: bool = False, exclude: list[str] = None
318
+ dir_path: Path | str,
319
+ recursive: bool = False,
320
+ exclude: list[str] = None,
295
321
  ) -> None:
296
322
  """
297
323
  Clears all files (and, if recursive, directories) in the specified directory,
@@ -376,7 +402,9 @@ class SysUtil:
376
402
  "Invalid filename. Ensure it doesn't contain illegal characters and has a valid extension."
377
403
  )
378
404
 
379
- name, ext = filename.rsplit(".", 1) if "." in filename else (filename, "")
405
+ name, ext = (
406
+ filename.rsplit(".", 1) if "." in filename else (filename, "")
407
+ )
380
408
  ext = f".{ext}" if ext else ""
381
409
 
382
410
  timestamp_str = ""
@@ -384,7 +412,9 @@ class SysUtil:
384
412
  timestamp_format = custom_timestamp_format or "%Y%m%d%H%M%S"
385
413
  timestamp_str = datetime.now().strftime(timestamp_format)
386
414
  filename = (
387
- f"{timestamp_str}_{name}" if time_prefix else f"{name}_{timestamp_str}"
415
+ f"{timestamp_str}_{name}"
416
+ if time_prefix
417
+ else f"{name}_{timestamp_str}"
388
418
  )
389
419
  else:
390
420
  filename = name
@@ -462,7 +492,9 @@ class SysUtil:
462
492
  if path.is_file():
463
493
  return path.stat().st_size
464
494
  elif path.is_dir():
465
- return sum(f.stat().st_size for f in path.glob("**/*") if f.is_file())
495
+ return sum(
496
+ f.stat().st_size for f in path.glob("**/*") if f.is_file()
497
+ )
466
498
  else:
467
499
  raise FileNotFoundError(f"{path} does not exist.")
468
500
 
@@ -6,7 +6,11 @@ from lionagi.core import Session
6
6
  from lionagi.libs import ParseUtil
7
7
 
8
8
  from .base_prompts import CODER_PROMPTS
9
- from .util import install_missing_dependencies, save_code_file, set_up_interpreter
9
+ from .util import (
10
+ install_missing_dependencies,
11
+ save_code_file,
12
+ set_up_interpreter,
13
+ )
10
14
 
11
15
 
12
16
  class Coder:
@@ -55,38 +59,50 @@ class Coder:
55
59
 
56
60
  async def _plan_code(self, context):
57
61
  print("Planning code...")
58
- plans = await self.session.chat(self.prompts["plan_code"], context=context)
62
+ plans = await self.session.chat(
63
+ self.prompts["plan_code"], context=context
64
+ )
59
65
  print("Code planning completed.")
60
66
  return plans
61
67
 
62
68
  async def _write_code(self, context=None):
63
69
  print("Writing code...")
64
- code = await self.session.chat(self.prompts["write_code"], context=context)
70
+ code = await self.session.chat(
71
+ self.prompts["write_code"], context=context
72
+ )
65
73
  print("Code writing completed.")
66
74
  return ParseUtil.extract_code_blocks(code)
67
75
 
68
76
  async def _review_code(self, context=None):
69
77
  print("Reviewing code...")
70
- code = await self.session.chat(self.prompts["review_code"], context=context)
78
+ code = await self.session.chat(
79
+ self.prompts["review_code"], context=context
80
+ )
71
81
  print("Code review completed.")
72
82
  return code
73
83
 
74
84
  async def _modify_code(self, context=None):
75
85
  print("Modifying code...")
76
- code = await self.session.chat(self.prompts["modify_code"], context=context)
86
+ code = await self.session.chat(
87
+ self.prompts["modify_code"], context=context
88
+ )
77
89
  print("Code modification completed.")
78
90
  return code
79
91
 
80
92
  async def _debug_code(self, context=None):
81
93
  print("Debugging code...")
82
- code = await self.session.chat(self.prompts["debug_code"], context=context)
94
+ code = await self.session.chat(
95
+ self.prompts["debug_code"], context=context
96
+ )
83
97
  print("Code debugging completed.")
84
98
  return code
85
99
 
86
100
  def _handle_execution_error(self, execution, required_libraries=None):
87
101
  print("Handling execution error...")
88
102
  if execution.error and execution.error.name == "ModuleNotFoundError":
89
- print("ModuleNotFoundError detected. Installing missing dependencies...")
103
+ print(
104
+ "ModuleNotFoundError detected. Installing missing dependencies..."
105
+ )
90
106
  install_missing_dependencies(required_libraries)
91
107
  print("Dependencies installed. Retrying execution.")
92
108
  return "try again"
@@ -118,7 +134,7 @@ class Coder:
118
134
  def _load_code_file(file_path):
119
135
  print("Loading code...")
120
136
  try:
121
- with open(file_path, "r") as file:
137
+ with open(file_path) as file:
122
138
  print("Code loaded.")
123
139
  return file.read()
124
140
  except FileNotFoundError:
@@ -28,7 +28,9 @@ def set_up_interpreter(interpreter_provider="e2b", key_scheme=E2B_key_scheme):
28
28
  def install_missing_dependencies(required_libraries):
29
29
  print("Checking for missing dependencies...")
30
30
  missing_libraries = [
31
- library for library in required_libraries if not is_library_installed(library)
31
+ library
32
+ for library in required_libraries
33
+ if not is_library_installed(library)
32
34
  ]
33
35
 
34
36
  if missing_libraries:
@@ -58,7 +60,9 @@ def install_library(library):
58
60
  import subprocess
59
61
  import sys
60
62
 
61
- subprocess.check_call([sys.executable, "-m", "pip", "install", library])
63
+ subprocess.check_call(
64
+ [sys.executable, "-m", "pip", "install", library]
65
+ )
62
66
  print(f"Successfully installed {library}.")
63
67
  except subprocess.CalledProcessError as e:
64
68
  print(f"Error occurred while installing {library}: {str(e)}")