lionagi 0.0.315__py3-none-any.whl → 0.1.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. lionagi/core/__init__.py +19 -8
  2. lionagi/core/agent/__init__.py +0 -3
  3. lionagi/core/agent/base_agent.py +26 -30
  4. lionagi/core/branch/__init__.py +0 -4
  5. lionagi/core/branch/{base_branch.py → base.py} +13 -14
  6. lionagi/core/branch/branch.py +22 -20
  7. lionagi/core/branch/executable_branch.py +0 -347
  8. lionagi/core/branch/{branch_flow_mixin.py → flow_mixin.py} +6 -6
  9. lionagi/core/branch/util.py +1 -1
  10. lionagi/core/direct/__init__.py +13 -1
  11. lionagi/core/direct/cot.py +123 -1
  12. lionagi/core/direct/plan.py +164 -0
  13. lionagi/core/direct/predict.py +13 -9
  14. lionagi/core/direct/react.py +12 -8
  15. lionagi/core/direct/score.py +4 -4
  16. lionagi/core/direct/select.py +4 -4
  17. lionagi/core/direct/utils.py +23 -0
  18. lionagi/core/direct/vote.py +2 -2
  19. lionagi/core/execute/base_executor.py +50 -0
  20. lionagi/core/execute/branch_executor.py +233 -0
  21. lionagi/core/execute/instruction_map_executor.py +131 -0
  22. lionagi/core/execute/structure_executor.py +218 -0
  23. lionagi/core/flow/monoflow/ReAct.py +4 -4
  24. lionagi/core/flow/monoflow/chat.py +6 -6
  25. lionagi/core/flow/monoflow/chat_mixin.py +24 -34
  26. lionagi/core/flow/monoflow/followup.py +4 -4
  27. lionagi/core/flow/polyflow/__init__.py +1 -1
  28. lionagi/core/flow/polyflow/chat.py +15 -12
  29. lionagi/core/{prompt/action_template.py → form/action_form.py} +2 -2
  30. lionagi/core/{prompt → form}/field_validator.py +40 -31
  31. lionagi/core/form/form.py +302 -0
  32. lionagi/core/form/mixin.py +214 -0
  33. lionagi/core/{prompt/scored_template.py → form/scored_form.py} +2 -2
  34. lionagi/core/generic/__init__.py +37 -0
  35. lionagi/core/generic/action.py +26 -0
  36. lionagi/core/generic/component.py +457 -0
  37. lionagi/core/generic/condition.py +44 -0
  38. lionagi/core/generic/data_logger.py +305 -0
  39. lionagi/core/generic/edge.py +110 -0
  40. lionagi/core/generic/mail.py +90 -0
  41. lionagi/core/generic/mailbox.py +36 -0
  42. lionagi/core/generic/node.py +285 -0
  43. lionagi/core/generic/relation.py +70 -0
  44. lionagi/core/generic/signal.py +22 -0
  45. lionagi/core/generic/structure.py +362 -0
  46. lionagi/core/generic/transfer.py +20 -0
  47. lionagi/core/generic/work.py +40 -0
  48. lionagi/core/graph/graph.py +126 -0
  49. lionagi/core/graph/tree.py +190 -0
  50. lionagi/core/mail/__init__.py +0 -8
  51. lionagi/core/mail/mail_manager.py +12 -10
  52. lionagi/core/mail/schema.py +9 -2
  53. lionagi/core/messages/__init__.py +0 -3
  54. lionagi/core/messages/schema.py +17 -225
  55. lionagi/core/session/__init__.py +0 -3
  56. lionagi/core/session/session.py +25 -23
  57. lionagi/core/tool/__init__.py +3 -1
  58. lionagi/core/tool/tool.py +28 -0
  59. lionagi/core/tool/tool_manager.py +75 -75
  60. lionagi/integrations/chunker/chunk.py +7 -7
  61. lionagi/integrations/config/oai_configs.py +4 -4
  62. lionagi/integrations/loader/load.py +6 -6
  63. lionagi/integrations/loader/load_util.py +8 -8
  64. lionagi/libs/ln_api.py +3 -3
  65. lionagi/libs/ln_parse.py +43 -6
  66. lionagi/libs/ln_validate.py +288 -0
  67. lionagi/libs/sys_util.py +28 -6
  68. lionagi/tests/libs/test_async.py +0 -0
  69. lionagi/tests/libs/test_field_validators.py +353 -0
  70. lionagi/tests/test_core/test_base_branch.py +0 -1
  71. lionagi/tests/test_core/test_branch.py +3 -0
  72. lionagi/tests/test_core/test_session_base_util.py +1 -0
  73. lionagi/version.py +1 -1
  74. {lionagi-0.0.315.dist-info → lionagi-0.1.0.dist-info}/METADATA +1 -1
  75. lionagi-0.1.0.dist-info/RECORD +136 -0
  76. lionagi/core/prompt/prompt_template.py +0 -312
  77. lionagi/core/schema/__init__.py +0 -22
  78. lionagi/core/schema/action_node.py +0 -29
  79. lionagi/core/schema/base_mixin.py +0 -296
  80. lionagi/core/schema/base_node.py +0 -199
  81. lionagi/core/schema/condition.py +0 -24
  82. lionagi/core/schema/data_logger.py +0 -354
  83. lionagi/core/schema/data_node.py +0 -93
  84. lionagi/core/schema/prompt_template.py +0 -67
  85. lionagi/core/schema/structure.py +0 -912
  86. lionagi/core/tool/manual.py +0 -1
  87. lionagi-0.0.315.dist-info/RECORD +0 -121
  88. /lionagi/core/{branch/base → execute}/__init__.py +0 -0
  89. /lionagi/core/flow/{base/baseflow.py → baseflow.py} +0 -0
  90. /lionagi/core/flow/{base/__init__.py → mono_chat_mixin.py} +0 -0
  91. /lionagi/core/{prompt → form}/__init__.py +0 -0
  92. /lionagi/{tests/test_integrations → core/graph}/__init__.py +0 -0
  93. /lionagi/tests/{test_libs → integrations}/__init__.py +0 -0
  94. /lionagi/tests/{test_libs/test_async.py → libs/__init__.py} +0 -0
  95. /lionagi/tests/{test_libs → libs}/test_api.py +0 -0
  96. /lionagi/tests/{test_libs → libs}/test_convert.py +0 -0
  97. /lionagi/tests/{test_libs → libs}/test_func_call.py +0 -0
  98. /lionagi/tests/{test_libs → libs}/test_nested.py +0 -0
  99. /lionagi/tests/{test_libs → libs}/test_parse.py +0 -0
  100. /lionagi/tests/{test_libs → libs}/test_sys_util.py +0 -0
  101. {lionagi-0.0.315.dist-info → lionagi-0.1.0.dist-info}/LICENSE +0 -0
  102. {lionagi-0.0.315.dist-info → lionagi-0.1.0.dist-info}/WHEEL +0 -0
  103. {lionagi-0.0.315.dist-info → lionagi-0.1.0.dist-info}/top_level.txt +0 -0
lionagi/libs/ln_parse.py CHANGED
@@ -1,11 +1,14 @@
1
+ from collections.abc import Callable
1
2
  import re
2
3
  import inspect
3
4
  import itertools
4
- from collections.abc import Callable
5
+ import contextlib
6
+ from functools import singledispatchmethod
5
7
  from typing import Any
6
8
  import numpy as np
7
9
  import lionagi.libs.ln_convert as convert
8
10
 
11
+
9
12
  md_json_char_map = {"\n": "\\n", "\r": "\\r", "\t": "\\t", '"': '\\"'}
10
13
 
11
14
 
@@ -590,14 +593,15 @@ class StringMatch:
590
593
  return d[m][n]
591
594
 
592
595
  @staticmethod
593
- def correct_keys(output_fields, out_, score_func=None):
596
+ def correct_dict_keys(keys: dict | list[str], dict_, score_func=None):
594
597
  if score_func is None:
595
598
  score_func = StringMatch.jaro_winkler_similarity
596
- fields_set = set(output_fields.keys())
599
+
600
+ fields_set = set(keys if isinstance(keys, list) else keys.keys())
597
601
  corrected_out = {}
598
602
  used_keys = set()
599
603
 
600
- for k, v in out_.items():
604
+ for k, v in dict_.items():
601
605
  if k in fields_set:
602
606
  corrected_out[k] = v
603
607
  fields_set.remove(k) # Remove the matched key
@@ -614,8 +618,8 @@ class StringMatch:
614
618
  fields_set.remove(best_match) # Remove the matched key
615
619
  used_keys.add(best_match)
616
620
 
617
- if len(used_keys) < len(out_):
618
- for k, v in out_.items():
621
+ if len(used_keys) < len(dict_):
622
+ for k, v in dict_.items():
619
623
  if k not in used_keys:
620
624
  corrected_out[k] = v
621
625
 
@@ -637,3 +641,36 @@ class StringMatch:
637
641
  # Find the index of the highest score
638
642
  max_score_index = np.argmax(scores)
639
643
  return correct_words_list[max_score_index]
644
+
645
+ @staticmethod
646
+ def force_validate_dict(x, keys: dict | list[str]) -> dict:
647
+ out_ = x
648
+
649
+ if isinstance(out_, str):
650
+ # first try to parse it straight as a fuzzy json
651
+ try:
652
+ out_ = ParseUtil.fuzzy_parse_json(out_)
653
+ except Exception:
654
+ try:
655
+ # if failed we try to extract the json block and parse it
656
+ out_ = ParseUtil.md_to_json(out_)
657
+ except Exception:
658
+ # if still failed we try to extract the json block using re and parse it again
659
+ match = re.search(r"```json\n({.*?})\n```", out_, re.DOTALL)
660
+ if match:
661
+ out_ = match.group(1)
662
+ try:
663
+ out_ = ParseUtil.fuzzy_parse_json(out_)
664
+ except:
665
+ try:
666
+ out_ = ParseUtil.fuzzy_parse_json(
667
+ out_.replace("'", '"')
668
+ )
669
+ except:
670
+ pass
671
+
672
+ if isinstance(out_, dict):
673
+ try:
674
+ return StringMatch.correct_dict_keys(keys, out_)
675
+ except Exception as e:
676
+ raise ValueError(f"Failed to force_validate_dict for input: {x}") from e
@@ -0,0 +1,288 @@
1
+ """
2
+ This module provides functions for validating and fixing field values based on their data types.
3
+
4
+ The module defines several functions for checking and fixing field values of different data types,
5
+ including numeric, boolean, string, and enum. It also provides a dictionary `validation_funcs` that
6
+ maps data types to their corresponding validation functions.
7
+ """
8
+
9
+ from .ln_convert import to_str, is_same_dtype, to_list, to_dict, to_num, strip_lower
10
+ from .ln_parse import StringMatch, ParseUtil
11
+
12
+
13
+ def check_dict_field(x, keys: list[str] | dict, fix_=True, **kwargs):
14
+ if isinstance(x, dict):
15
+ return x
16
+ if fix_:
17
+ try:
18
+ x = to_str(x)
19
+ return StringMatch.force_validate_dict(x, keys=keys, **kwargs)
20
+ except Exception as e:
21
+ raise ValueError("Invalid dict field type.") from e
22
+ raise ValueError(f"Default value for DICT must be a dict, got {type(x).__name__}")
23
+
24
+
25
+ def check_action_field(x, fix_=True, **kwargs):
26
+ if (
27
+ isinstance(x, list)
28
+ and is_same_dtype(x, dict)
29
+ and all(_has_action_keys(y) for y in x)
30
+ ):
31
+ return x
32
+ try:
33
+ x = _fix_action_field(x, fix_)
34
+ return x
35
+ except Exception as e:
36
+ raise ValueError("Invalid action field type.") from e
37
+
38
+
39
+ def check_number_field(x, fix_=True, **kwargs):
40
+ """
41
+ Checks if the given value is a valid numeric field.
42
+
43
+ Args:
44
+ x: The value to check.
45
+ fix_ (bool): Flag indicating whether to attempt fixing the value if it's invalid (default: True).
46
+ **kwargs: Additional keyword arguments for fixing the value.
47
+
48
+ Returns:
49
+ The original value if it's valid, or the fixed value if `fix_` is True.
50
+
51
+ Raises:
52
+ ValueError: If the value is not a valid numeric field and cannot be fixed.
53
+ """
54
+ if not isinstance(x, (int, float)):
55
+ if fix_:
56
+ try:
57
+ return _fix_number_field(x, **kwargs)
58
+ except Exception as e:
59
+ raise e
60
+
61
+ raise ValueError(
62
+ f"Default value for NUMERIC must be an int or float, got {type(x).__name__}"
63
+ )
64
+ return x
65
+
66
+
67
+ def check_bool_field(x, fix_=True):
68
+ """
69
+ Checks if the given value is a valid boolean field.
70
+
71
+ Args:
72
+ x: The value to check.
73
+ fix_ (bool): Flag indicating whether to attempt fixing the value if it's invalid (default: True).
74
+
75
+ Returns:
76
+ The original value if it's valid, or the fixed value if `fix_` is True.
77
+
78
+ Raises:
79
+ ValueError: If the value is not a valid boolean field and cannot be fixed.
80
+ """
81
+ if not isinstance(x, bool):
82
+ if fix_:
83
+ try:
84
+ return _fix_bool_field(x)
85
+ except Exception as e:
86
+ raise e
87
+
88
+ raise ValueError(
89
+ f"Default value for BOOLEAN must be a bool, got {type(x).__name__}"
90
+ )
91
+ return x
92
+
93
+
94
+ def check_str_field(x, *args, fix_=True, **kwargs):
95
+ """
96
+ Checks if the given value is a valid string field.
97
+
98
+ Args:
99
+ x: The value to check.
100
+ *args: Additional positional arguments for fixing the value.
101
+ fix_ (bool): Flag indicating whether to attempt fixing the value if it's invalid (default: True).
102
+ **kwargs: Additional keyword arguments for fixing the value.
103
+
104
+ Returns:
105
+ The original value if it's valid, or the fixed value if `fix_` is True.
106
+
107
+ Raises:
108
+ ValueError: If the value is not a valid string field and cannot be fixed.
109
+ """
110
+ if not isinstance(x, str):
111
+ if fix_:
112
+ try:
113
+ return _fix_str_field(x, *args, **kwargs)
114
+ except Exception as e:
115
+ raise e
116
+
117
+ raise ValueError(
118
+ f"Default value for STRING must be a str, got {type(x).__name__}"
119
+ )
120
+ return x
121
+
122
+
123
+ def check_enum_field(x, choices, fix_=True, **kwargs):
124
+ """
125
+ Checks if the given value is a valid enum field.
126
+
127
+ Args:
128
+ x: The value to check.
129
+ choices: The list of valid choices for the enum field.
130
+ fix_ (bool): Flag indicating whether to attempt fixing the value if it's invalid (default: True).
131
+ **kwargs: Additional keyword arguments for fixing the value.
132
+
133
+ Returns:
134
+ The original value if it's valid, or the fixed value if `fix_` is True.
135
+
136
+ Raises:
137
+ ValueError: If the value is not a valid enum field and cannot be fixed.
138
+ """
139
+ same_dtype, dtype_ = is_same_dtype(choices, return_dtype=True)
140
+ if not same_dtype:
141
+ raise ValueError(
142
+ f"Field type ENUM requires all choices to be of the same type, got {choices}"
143
+ )
144
+
145
+ if not isinstance(x, dtype_):
146
+ raise ValueError(
147
+ f"Default value for ENUM must be an instance of the {dtype_.__name__}, got {type(x).__name__}"
148
+ )
149
+
150
+ if x not in choices:
151
+ if fix_:
152
+ try:
153
+ return _fix_enum_field(x, choices, **kwargs)
154
+ except Exception as e:
155
+ raise e
156
+ raise ValueError(
157
+ f"Default value for ENUM must be one of the {choices}, got {x}"
158
+ )
159
+
160
+ return x
161
+
162
+
163
+ def _has_action_keys(dict_):
164
+ return list(dict_.keys()) >= ["function", "arguments"]
165
+
166
+
167
+ def _fix_action_field(x, discard_=True):
168
+ corrected = []
169
+ if isinstance(x, str):
170
+ x = ParseUtil.fuzzy_parse_json(x)
171
+
172
+ try:
173
+ x = to_list(x)
174
+
175
+ for i in x:
176
+ i = to_dict(i)
177
+ if _has_action_keys(i):
178
+ corrected.append(i)
179
+ elif not discard_:
180
+ raise ValueError(f"Invalid action field: {i}")
181
+ except Exception as e:
182
+ raise ValueError(f"Invalid action field: {e}") from e
183
+
184
+ return corrected
185
+
186
+
187
+ def _fix_number_field(x, *args, **kwargs):
188
+ """
189
+ Attempts to fix an invalid numeric field value.
190
+
191
+ Args:
192
+ x: The value to fix.
193
+ *args: Additional positional arguments for fixing the value.
194
+ **kwargs: Additional keyword arguments for fixing the value.
195
+
196
+ Returns:
197
+ The fixed numeric value.
198
+
199
+ Raises:
200
+ ValueError: If the value cannot be converted into a valid numeric value.
201
+ """
202
+ try:
203
+ x = to_num(x, *args, **kwargs)
204
+ if isinstance(x, (int, float)):
205
+ return x
206
+ raise ValueError(f"Failed to convert {x} into a numeric value")
207
+ except Exception as e:
208
+ raise ValueError(f"Failed to convert {x} into a numeric value") from e
209
+
210
+
211
+ def _fix_bool_field(x):
212
+ """
213
+ Attempts to fix an invalid boolean field value.
214
+
215
+ Args:
216
+ x: The value to fix.
217
+
218
+ Returns:
219
+ The fixed boolean value.
220
+
221
+ Raises:
222
+ ValueError: If the value cannot be converted into a valid boolean value.
223
+ """
224
+ try:
225
+ x = strip_lower(to_str(x))
226
+ if x in ["true", "1", "correct", "yes"]:
227
+ return True
228
+
229
+ elif x in ["false", "0", "incorrect", "no", "none", "n/a"]:
230
+ return False
231
+
232
+ raise ValueError(f"Failed to convert {x} into a boolean value")
233
+ except Exception as e:
234
+ raise ValueError(f"Failed to convert {x} into a boolean value") from e
235
+
236
+
237
+ def _fix_str_field(x):
238
+ """
239
+ Attempts to fix an invalid string field value.
240
+
241
+ Args:
242
+ x: The value to fix.
243
+
244
+ Returns:
245
+ The fixed string value.
246
+
247
+ Raises:
248
+ ValueError: If the value cannot be converted into a valid string value.
249
+ """
250
+ try:
251
+ x = to_str(x)
252
+ if isinstance(x, str):
253
+ return x
254
+ raise ValueError(f"Failed to convert {x} into a string value")
255
+ except Exception as e:
256
+ raise ValueError(f"Failed to convert {x} into a string value") from e
257
+
258
+
259
+ def _fix_enum_field(x, choices, **kwargs):
260
+ """
261
+ Attempts to fix an invalid enum field value.
262
+
263
+ Args:
264
+ x: The value to fix.
265
+ choices: The list of valid choices for the enum field.
266
+ **kwargs: Additional keyword arguments for fixing the value.
267
+
268
+ Returns:
269
+ The fixed enum value.
270
+
271
+ Raises:
272
+ ValueError: If the value cannot be converted into a valid enum value.
273
+ """
274
+ try:
275
+ x = to_str(x)
276
+ return StringMatch.choose_most_similar(x, choices, **kwargs)
277
+ except Exception as e:
278
+ raise ValueError(f"Failed to convert {x} into one of the choices") from e
279
+
280
+
281
+ validation_funcs = {
282
+ "number": check_number_field,
283
+ "bool": check_bool_field,
284
+ "str": check_str_field,
285
+ "enum": check_enum_field,
286
+ "action": check_action_field,
287
+ "dict": check_dict_field,
288
+ }
lionagi/libs/sys_util.py CHANGED
@@ -3,11 +3,26 @@ MIT License
3
3
 
4
4
  Copyright (c) 2023 HaiyangLi quantocean.li@gmail.com
5
5
 
6
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7
-
8
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9
-
10
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
6
+ Permission is hereby granted, free of charge, to any person
7
+ obtaining a copy of this software and associated documentation
8
+ files (the "Software"), to deal in the Software without
9
+ restriction, including without limitation the rights to use,
10
+ copy, modify, merge, publish, distribute, sublicense, and/or
11
+ sell copies of the Software, and to permit persons to whom
12
+ the Software is furnished to do so, subject to the following
13
+ conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
22
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
11
26
  """
12
27
 
13
28
  import copy
@@ -363,6 +378,7 @@ class SysUtil:
363
378
  dir_exist_ok: bool = True,
364
379
  time_prefix: bool = False,
365
380
  custom_timestamp_format: str | None = None,
381
+ random_hash_digits=0,
366
382
  ) -> Path:
367
383
  """
368
384
  Creates a path with an optional timestamp in the specified directory.
@@ -400,7 +416,13 @@ class SysUtil:
400
416
  else:
401
417
  filename = name
402
418
 
403
- full_filename = f"{filename}{ext}"
419
+ random_hash = (
420
+ "-" + SysUtil.create_id(random_hash_digits)
421
+ if random_hash_digits > 0
422
+ else ""
423
+ )
424
+
425
+ full_filename = f"{filename}{random_hash}{ext}"
404
426
  full_path = directory / full_filename
405
427
  full_path.parent.mkdir(parents=True, exist_ok=dir_exist_ok)
406
428
 
File without changes