lionagi 0.0.312__py3-none-any.whl → 0.2.1__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.
- lionagi/__init__.py +61 -3
- lionagi/core/__init__.py +0 -14
- lionagi/core/_setting/_setting.py +59 -0
- lionagi/core/action/__init__.py +14 -0
- lionagi/core/action/function_calling.py +136 -0
- lionagi/core/action/manual.py +1 -0
- lionagi/core/action/node.py +109 -0
- lionagi/core/action/tool.py +114 -0
- lionagi/core/action/tool_manager.py +356 -0
- lionagi/core/agent/__init__.py +0 -3
- lionagi/core/agent/base_agent.py +45 -36
- lionagi/core/agent/eval/evaluator.py +1 -0
- lionagi/core/agent/eval/vote.py +40 -0
- lionagi/core/agent/learn/learner.py +59 -0
- lionagi/core/agent/plan/unit_template.py +1 -0
- lionagi/core/collections/__init__.py +17 -0
- lionagi/core/collections/_logger.py +319 -0
- lionagi/core/collections/abc/__init__.py +53 -0
- lionagi/core/collections/abc/component.py +615 -0
- lionagi/core/collections/abc/concepts.py +297 -0
- lionagi/core/collections/abc/exceptions.py +150 -0
- lionagi/core/collections/abc/util.py +45 -0
- lionagi/core/collections/exchange.py +161 -0
- lionagi/core/collections/flow.py +426 -0
- lionagi/core/collections/model.py +419 -0
- lionagi/core/collections/pile.py +913 -0
- lionagi/core/collections/progression.py +236 -0
- lionagi/core/collections/util.py +64 -0
- lionagi/core/director/direct.py +314 -0
- lionagi/core/director/director.py +2 -0
- lionagi/core/engine/branch_engine.py +333 -0
- lionagi/core/engine/instruction_map_engine.py +204 -0
- lionagi/core/engine/sandbox_.py +14 -0
- lionagi/core/engine/script_engine.py +99 -0
- lionagi/core/executor/base_executor.py +90 -0
- lionagi/core/executor/graph_executor.py +330 -0
- lionagi/core/executor/neo4j_executor.py +384 -0
- lionagi/core/generic/__init__.py +7 -0
- lionagi/core/generic/edge.py +112 -0
- lionagi/core/generic/edge_condition.py +16 -0
- lionagi/core/generic/graph.py +236 -0
- lionagi/core/generic/hyperedge.py +1 -0
- lionagi/core/generic/node.py +220 -0
- lionagi/core/generic/tree.py +48 -0
- lionagi/core/generic/tree_node.py +79 -0
- lionagi/core/mail/__init__.py +7 -3
- lionagi/core/mail/mail.py +25 -0
- lionagi/core/mail/mail_manager.py +142 -58
- lionagi/core/mail/package.py +45 -0
- lionagi/core/mail/start_mail.py +36 -0
- lionagi/core/message/__init__.py +19 -0
- lionagi/core/message/action_request.py +133 -0
- lionagi/core/message/action_response.py +135 -0
- lionagi/core/message/assistant_response.py +95 -0
- lionagi/core/message/instruction.py +234 -0
- lionagi/core/message/message.py +101 -0
- lionagi/core/message/system.py +86 -0
- lionagi/core/message/util.py +283 -0
- lionagi/core/report/__init__.py +4 -0
- lionagi/core/report/base.py +217 -0
- lionagi/core/report/form.py +231 -0
- lionagi/core/report/report.py +166 -0
- lionagi/core/report/util.py +28 -0
- lionagi/core/rule/__init__.py +0 -0
- lionagi/core/rule/_default.py +16 -0
- lionagi/core/rule/action.py +99 -0
- lionagi/core/rule/base.py +238 -0
- lionagi/core/rule/boolean.py +56 -0
- lionagi/core/rule/choice.py +47 -0
- lionagi/core/rule/mapping.py +96 -0
- lionagi/core/rule/number.py +71 -0
- lionagi/core/rule/rulebook.py +109 -0
- lionagi/core/rule/string.py +52 -0
- lionagi/core/rule/util.py +35 -0
- lionagi/core/session/__init__.py +0 -3
- lionagi/core/session/branch.py +431 -0
- lionagi/core/session/directive_mixin.py +287 -0
- lionagi/core/session/session.py +230 -902
- lionagi/core/structure/__init__.py +1 -0
- lionagi/core/structure/chain.py +1 -0
- lionagi/core/structure/forest.py +1 -0
- lionagi/core/structure/graph.py +1 -0
- lionagi/core/structure/tree.py +1 -0
- lionagi/core/unit/__init__.py +5 -0
- lionagi/core/unit/parallel_unit.py +245 -0
- lionagi/core/unit/template/__init__.py +0 -0
- lionagi/core/unit/template/action.py +81 -0
- lionagi/core/unit/template/base.py +51 -0
- lionagi/core/unit/template/plan.py +84 -0
- lionagi/core/unit/template/predict.py +109 -0
- lionagi/core/unit/template/score.py +124 -0
- lionagi/core/unit/template/select.py +104 -0
- lionagi/core/unit/unit.py +362 -0
- lionagi/core/unit/unit_form.py +305 -0
- lionagi/core/unit/unit_mixin.py +1168 -0
- lionagi/core/unit/util.py +71 -0
- lionagi/core/validator/__init__.py +0 -0
- lionagi/core/validator/validator.py +364 -0
- lionagi/core/work/__init__.py +0 -0
- lionagi/core/work/work.py +76 -0
- lionagi/core/work/work_function.py +101 -0
- lionagi/core/work/work_queue.py +103 -0
- lionagi/core/work/worker.py +258 -0
- lionagi/core/work/worklog.py +120 -0
- lionagi/experimental/__init__.py +0 -0
- lionagi/experimental/compressor/__init__.py +0 -0
- lionagi/experimental/compressor/base.py +46 -0
- lionagi/experimental/compressor/llm_compressor.py +247 -0
- lionagi/experimental/compressor/llm_summarizer.py +61 -0
- lionagi/experimental/compressor/util.py +70 -0
- lionagi/experimental/directive/__init__.py +19 -0
- lionagi/experimental/directive/parser/__init__.py +0 -0
- lionagi/experimental/directive/parser/base_parser.py +282 -0
- lionagi/experimental/directive/template/__init__.py +0 -0
- lionagi/experimental/directive/template/base_template.py +79 -0
- lionagi/experimental/directive/template/schema.py +36 -0
- lionagi/experimental/directive/tokenizer.py +73 -0
- lionagi/experimental/evaluator/__init__.py +0 -0
- lionagi/experimental/evaluator/ast_evaluator.py +131 -0
- lionagi/experimental/evaluator/base_evaluator.py +218 -0
- lionagi/experimental/knowledge/__init__.py +0 -0
- lionagi/experimental/knowledge/base.py +10 -0
- lionagi/experimental/knowledge/graph.py +0 -0
- lionagi/experimental/memory/__init__.py +0 -0
- lionagi/experimental/strategies/__init__.py +0 -0
- lionagi/experimental/strategies/base.py +1 -0
- lionagi/integrations/bridge/autogen_/__init__.py +0 -0
- lionagi/integrations/bridge/autogen_/autogen_.py +124 -0
- lionagi/integrations/bridge/langchain_/documents.py +4 -0
- lionagi/integrations/bridge/llamaindex_/index.py +30 -0
- lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +6 -0
- lionagi/integrations/bridge/llamaindex_/llama_pack.py +227 -0
- lionagi/integrations/bridge/llamaindex_/node_parser.py +6 -9
- lionagi/integrations/bridge/pydantic_/pydantic_bridge.py +1 -0
- lionagi/integrations/bridge/transformers_/__init__.py +0 -0
- lionagi/integrations/bridge/transformers_/install_.py +36 -0
- lionagi/integrations/chunker/__init__.py +0 -0
- lionagi/integrations/chunker/chunk.py +312 -0
- lionagi/integrations/config/oai_configs.py +38 -7
- lionagi/integrations/config/ollama_configs.py +1 -1
- lionagi/integrations/config/openrouter_configs.py +14 -2
- lionagi/integrations/loader/__init__.py +0 -0
- lionagi/integrations/loader/load.py +253 -0
- lionagi/integrations/loader/load_util.py +195 -0
- lionagi/integrations/provider/_mapping.py +46 -0
- lionagi/integrations/provider/litellm.py +2 -1
- lionagi/integrations/provider/mlx_service.py +16 -9
- lionagi/integrations/provider/oai.py +91 -4
- lionagi/integrations/provider/ollama.py +7 -6
- lionagi/integrations/provider/openrouter.py +115 -8
- lionagi/integrations/provider/services.py +2 -2
- lionagi/integrations/provider/transformers.py +18 -22
- lionagi/integrations/storage/__init__.py +3 -0
- lionagi/integrations/storage/neo4j.py +665 -0
- lionagi/integrations/storage/storage_util.py +287 -0
- lionagi/integrations/storage/structure_excel.py +285 -0
- lionagi/integrations/storage/to_csv.py +63 -0
- lionagi/integrations/storage/to_excel.py +83 -0
- lionagi/libs/__init__.py +26 -1
- lionagi/libs/ln_api.py +78 -23
- lionagi/libs/ln_context.py +37 -0
- lionagi/libs/ln_convert.py +21 -9
- lionagi/libs/ln_func_call.py +69 -28
- lionagi/libs/ln_image.py +107 -0
- lionagi/libs/ln_knowledge_graph.py +405 -0
- lionagi/libs/ln_nested.py +26 -11
- lionagi/libs/ln_parse.py +110 -14
- lionagi/libs/ln_queue.py +117 -0
- lionagi/libs/ln_tokenize.py +164 -0
- lionagi/{core/prompt/field_validator.py → libs/ln_validate.py} +79 -14
- lionagi/libs/special_tokens.py +172 -0
- lionagi/libs/sys_util.py +107 -2
- lionagi/lions/__init__.py +0 -0
- lionagi/lions/coder/__init__.py +0 -0
- lionagi/lions/coder/add_feature.py +20 -0
- lionagi/lions/coder/base_prompts.py +22 -0
- lionagi/lions/coder/code_form.py +13 -0
- lionagi/lions/coder/coder.py +168 -0
- lionagi/lions/coder/util.py +96 -0
- lionagi/lions/researcher/__init__.py +0 -0
- lionagi/lions/researcher/data_source/__init__.py +0 -0
- lionagi/lions/researcher/data_source/finhub_.py +191 -0
- lionagi/lions/researcher/data_source/google_.py +199 -0
- lionagi/lions/researcher/data_source/wiki_.py +96 -0
- lionagi/lions/researcher/data_source/yfinance_.py +21 -0
- lionagi/tests/integrations/__init__.py +0 -0
- lionagi/tests/libs/__init__.py +0 -0
- lionagi/tests/libs/test_field_validators.py +353 -0
- lionagi/tests/{test_libs → libs}/test_func_call.py +23 -21
- lionagi/tests/{test_libs → libs}/test_nested.py +36 -21
- lionagi/tests/{test_libs → libs}/test_parse.py +1 -1
- lionagi/tests/libs/test_queue.py +67 -0
- lionagi/tests/test_core/collections/__init__.py +0 -0
- lionagi/tests/test_core/collections/test_component.py +206 -0
- lionagi/tests/test_core/collections/test_exchange.py +138 -0
- lionagi/tests/test_core/collections/test_flow.py +145 -0
- lionagi/tests/test_core/collections/test_pile.py +171 -0
- lionagi/tests/test_core/collections/test_progression.py +129 -0
- lionagi/tests/test_core/generic/__init__.py +0 -0
- lionagi/tests/test_core/generic/test_edge.py +67 -0
- lionagi/tests/test_core/generic/test_graph.py +96 -0
- lionagi/tests/test_core/generic/test_node.py +106 -0
- lionagi/tests/test_core/generic/test_tree_node.py +73 -0
- lionagi/tests/test_core/test_branch.py +115 -292
- lionagi/tests/test_core/test_form.py +46 -0
- lionagi/tests/test_core/test_report.py +105 -0
- lionagi/tests/test_core/test_validator.py +111 -0
- lionagi/version.py +1 -1
- {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/LICENSE +12 -11
- {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/METADATA +19 -118
- lionagi-0.2.1.dist-info/RECORD +240 -0
- lionagi/core/branch/__init__.py +0 -4
- lionagi/core/branch/base_branch.py +0 -654
- lionagi/core/branch/branch.py +0 -471
- lionagi/core/branch/branch_flow_mixin.py +0 -96
- lionagi/core/branch/executable_branch.py +0 -347
- lionagi/core/branch/util.py +0 -323
- lionagi/core/direct/__init__.py +0 -6
- lionagi/core/direct/predict.py +0 -161
- lionagi/core/direct/score.py +0 -278
- lionagi/core/direct/select.py +0 -169
- lionagi/core/direct/utils.py +0 -87
- lionagi/core/direct/vote.py +0 -64
- lionagi/core/flow/base/baseflow.py +0 -23
- lionagi/core/flow/monoflow/ReAct.py +0 -238
- lionagi/core/flow/monoflow/__init__.py +0 -9
- lionagi/core/flow/monoflow/chat.py +0 -95
- lionagi/core/flow/monoflow/chat_mixin.py +0 -263
- lionagi/core/flow/monoflow/followup.py +0 -214
- lionagi/core/flow/polyflow/__init__.py +0 -1
- lionagi/core/flow/polyflow/chat.py +0 -248
- lionagi/core/mail/schema.py +0 -56
- lionagi/core/messages/__init__.py +0 -3
- lionagi/core/messages/schema.py +0 -533
- lionagi/core/prompt/prompt_template.py +0 -316
- lionagi/core/schema/__init__.py +0 -22
- lionagi/core/schema/action_node.py +0 -29
- lionagi/core/schema/base_mixin.py +0 -296
- lionagi/core/schema/base_node.py +0 -199
- lionagi/core/schema/condition.py +0 -24
- lionagi/core/schema/data_logger.py +0 -354
- lionagi/core/schema/data_node.py +0 -93
- lionagi/core/schema/prompt_template.py +0 -67
- lionagi/core/schema/structure.py +0 -910
- lionagi/core/tool/__init__.py +0 -3
- lionagi/core/tool/tool_manager.py +0 -280
- lionagi/integrations/bridge/pydantic_/base_model.py +0 -7
- lionagi/tests/test_core/test_base_branch.py +0 -427
- lionagi/tests/test_core/test_chat_flow.py +0 -63
- lionagi/tests/test_core/test_mail_manager.py +0 -75
- lionagi/tests/test_core/test_prompts.py +0 -51
- lionagi/tests/test_core/test_session.py +0 -254
- lionagi/tests/test_core/test_session_base_util.py +0 -312
- lionagi/tests/test_core/test_tool_manager.py +0 -95
- lionagi-0.0.312.dist-info/RECORD +0 -111
- /lionagi/core/{branch/base → _setting}/__init__.py +0 -0
- /lionagi/core/{flow → agent/eval}/__init__.py +0 -0
- /lionagi/core/{flow/base → agent/learn}/__init__.py +0 -0
- /lionagi/core/{prompt → agent/plan}/__init__.py +0 -0
- /lionagi/core/{tool/manual.py → agent/plan/plan.py} +0 -0
- /lionagi/{tests/test_integrations → core/director}/__init__.py +0 -0
- /lionagi/{tests/test_libs → core/engine}/__init__.py +0 -0
- /lionagi/{tests/test_libs/test_async.py → core/executor/__init__.py} +0 -0
- /lionagi/tests/{test_libs → libs}/test_api.py +0 -0
- /lionagi/tests/{test_libs → libs}/test_convert.py +0 -0
- /lionagi/tests/{test_libs → libs}/test_sys_util.py +0 -0
- {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/WHEEL +0 -0
- {lionagi-0.0.312.dist-info → lionagi-0.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,238 @@
|
|
1
|
+
"""
|
2
|
+
Copyright 2024 HaiyangLi
|
3
|
+
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
you may not use this file except in compliance with the License.
|
6
|
+
You may obtain a copy of the License at
|
7
|
+
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
See the License for the specific language governing permissions and
|
14
|
+
limitations under the License.
|
15
|
+
"""
|
16
|
+
|
17
|
+
from abc import abstractmethod
|
18
|
+
from typing import Any, List, Dict
|
19
|
+
from pandas import Series
|
20
|
+
from lionagi.libs import SysUtil
|
21
|
+
|
22
|
+
from lionagi.core.collections.abc import FieldError, Condition, Actionable, Component
|
23
|
+
|
24
|
+
_rule_classes = {}
|
25
|
+
|
26
|
+
|
27
|
+
class Rule(Component, Condition, Actionable):
|
28
|
+
"""
|
29
|
+
Combines a condition and an action that can be applied based on it.
|
30
|
+
|
31
|
+
Attributes:
|
32
|
+
apply_type (str): The type of data to which the rule applies.
|
33
|
+
fix (bool): Indicates whether the rule includes a fix action.
|
34
|
+
fields (list[str]): List of fields to which the rule applies.
|
35
|
+
validation_kwargs (dict): Keyword arguments for validation.
|
36
|
+
applied_log (list): Log of applied rules.
|
37
|
+
invoked_log (list): Log of invoked rules.
|
38
|
+
_is_init (bool): Indicates whether the rule is initialized.
|
39
|
+
"""
|
40
|
+
|
41
|
+
exclude_type: list[str] = []
|
42
|
+
apply_type: list[str] | str = None
|
43
|
+
fix: bool = True
|
44
|
+
fields: list[str] = []
|
45
|
+
validation_kwargs: dict = {}
|
46
|
+
applied_log: list = []
|
47
|
+
invoked_log: list = []
|
48
|
+
_is_init: bool = False
|
49
|
+
|
50
|
+
def __init_subclass__(cls, **kwargs):
|
51
|
+
super().__init_subclass__(**kwargs)
|
52
|
+
if cls.__name__ not in _rule_classes:
|
53
|
+
_rule_classes[cls.__name__] = cls
|
54
|
+
|
55
|
+
def add_log(self, field: str, form: Any, apply: bool = True, **kwargs) -> None:
|
56
|
+
"""
|
57
|
+
Adds an entry to the applied or invoked log.
|
58
|
+
|
59
|
+
Args:
|
60
|
+
field (str): The field being validated.
|
61
|
+
form (Any): The form being validated.
|
62
|
+
apply (bool): Indicates whether the log is for an applied rule.
|
63
|
+
**kwargs: Additional configuration parameters.
|
64
|
+
"""
|
65
|
+
a = {
|
66
|
+
"type": "rule",
|
67
|
+
"class": self.class_name,
|
68
|
+
"ln_id": self.ln_id,
|
69
|
+
"timestamp": SysUtil.get_timestamp(sep=None)[:-6],
|
70
|
+
"field": field,
|
71
|
+
"form": form.ln_id,
|
72
|
+
"config": kwargs,
|
73
|
+
}
|
74
|
+
if apply:
|
75
|
+
self.applied_log.append(a)
|
76
|
+
else:
|
77
|
+
self.invoked_log.append(a)
|
78
|
+
|
79
|
+
async def applies(
|
80
|
+
self,
|
81
|
+
field: str,
|
82
|
+
value: Any,
|
83
|
+
form: Any,
|
84
|
+
*args,
|
85
|
+
annotation: List[str] = None,
|
86
|
+
use_annotation: bool = True,
|
87
|
+
**kwargs,
|
88
|
+
) -> bool:
|
89
|
+
"""
|
90
|
+
Determines whether the rule applies to a given field and value.
|
91
|
+
|
92
|
+
Args:
|
93
|
+
field (str): The field being validated.
|
94
|
+
value (Any): The value of the field.
|
95
|
+
form (Any): The form being validated.
|
96
|
+
*args: Additional arguments.
|
97
|
+
annotation (list[str], optional): Annotations for the field.
|
98
|
+
use_annotation (bool): Indicates whether to use annotations.
|
99
|
+
**kwargs: Additional keyword arguments.
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
bool: True if the rule applies, otherwise False.
|
103
|
+
"""
|
104
|
+
if self.fields:
|
105
|
+
if field in self.fields:
|
106
|
+
self.add_log(field, form, **kwargs)
|
107
|
+
return True
|
108
|
+
|
109
|
+
if use_annotation:
|
110
|
+
annotation = annotation or form._get_field_annotation(field)
|
111
|
+
annotation = [annotation] if isinstance(annotation, str) else annotation
|
112
|
+
|
113
|
+
for i in annotation:
|
114
|
+
if i in self.apply_type and i not in self.exclude_type:
|
115
|
+
self.add_log(field, form, **kwargs)
|
116
|
+
return True
|
117
|
+
return False
|
118
|
+
|
119
|
+
a = await self.rule_condition(field, value, *args, **kwargs)
|
120
|
+
|
121
|
+
if a:
|
122
|
+
self.add_log(field, form, **kwargs)
|
123
|
+
return True
|
124
|
+
return False
|
125
|
+
|
126
|
+
async def invoke(self, field: str, value: Any, form: Any) -> Any:
|
127
|
+
"""
|
128
|
+
Invokes the rule's validation logic on a field and value.
|
129
|
+
|
130
|
+
Args:
|
131
|
+
field (str): The field being validated.
|
132
|
+
value (Any): The value of the field.
|
133
|
+
form (Any): The form being validated.
|
134
|
+
|
135
|
+
Returns:
|
136
|
+
Any: The validated or fixed value.
|
137
|
+
|
138
|
+
Raises:
|
139
|
+
ValueError: If validation or fixing fails.
|
140
|
+
"""
|
141
|
+
try:
|
142
|
+
a = await self.validate(value, **self.validation_kwargs)
|
143
|
+
self.add_log(field, form, apply=False, **self.validation_kwargs)
|
144
|
+
return a
|
145
|
+
|
146
|
+
except Exception as e1:
|
147
|
+
if self.fix:
|
148
|
+
try:
|
149
|
+
a = await self.perform_fix(value, **self.validation_kwargs)
|
150
|
+
self.add_log(field, form, apply=False, **self.validation_kwargs)
|
151
|
+
return a
|
152
|
+
except Exception as e2:
|
153
|
+
raise FieldError(f"failed to fix field") from e2
|
154
|
+
raise FieldError(f"failed to validate field") from e1
|
155
|
+
|
156
|
+
async def rule_condition(self, field, value, *args, **kwargs) -> bool:
|
157
|
+
"""
|
158
|
+
Additional condition, if choosing not to use annotation as a qualifier.
|
159
|
+
|
160
|
+
Args:
|
161
|
+
*args: Additional arguments.
|
162
|
+
**kwargs: Additional keyword arguments.
|
163
|
+
|
164
|
+
Returns:
|
165
|
+
bool: False by default, should be overridden by subclasses.
|
166
|
+
"""
|
167
|
+
return False
|
168
|
+
|
169
|
+
async def perform_fix(self, value: Any, *args, **kwargs) -> Any:
|
170
|
+
"""
|
171
|
+
Attempts to fix a value if validation fails.
|
172
|
+
|
173
|
+
Args:
|
174
|
+
value (Any): The value to fix.
|
175
|
+
*args: Additional arguments.
|
176
|
+
**kwargs: Additional keyword arguments.
|
177
|
+
|
178
|
+
Returns:
|
179
|
+
Any: The fixed value.
|
180
|
+
|
181
|
+
Raises:
|
182
|
+
ValueError: If the fix fails.
|
183
|
+
"""
|
184
|
+
return value
|
185
|
+
|
186
|
+
@abstractmethod
|
187
|
+
async def validate(self, value: Any) -> Any:
|
188
|
+
"""
|
189
|
+
Abstract method to validate a value.
|
190
|
+
|
191
|
+
Args:
|
192
|
+
value (Any): The value to validate.
|
193
|
+
|
194
|
+
Returns:
|
195
|
+
Any: The validated value.
|
196
|
+
|
197
|
+
Raises:
|
198
|
+
ValueError: If validation fails.
|
199
|
+
"""
|
200
|
+
pass
|
201
|
+
|
202
|
+
def _to_dict(self) -> Dict[str, Any]:
|
203
|
+
"""
|
204
|
+
Converts the rule's attributes to a dictionary.
|
205
|
+
|
206
|
+
Returns:
|
207
|
+
dict: A dictionary representation of the rule.
|
208
|
+
"""
|
209
|
+
return {
|
210
|
+
"ln_id": self.ln_id[:8] + "...",
|
211
|
+
"rule": self.__class__.__name__,
|
212
|
+
"apply_type": self.apply_type,
|
213
|
+
"fix": self.fix,
|
214
|
+
"fields": self.fields,
|
215
|
+
"validation_kwargs": self.validation_kwargs,
|
216
|
+
"num_applied": len(self.applied_log),
|
217
|
+
"num_invoked": len(self.invoked_log),
|
218
|
+
}
|
219
|
+
|
220
|
+
def __str__(self) -> str:
|
221
|
+
"""
|
222
|
+
Returns a string representation of the rule using a pandas Series.
|
223
|
+
|
224
|
+
Returns:
|
225
|
+
str: A string representation of the rule.
|
226
|
+
"""
|
227
|
+
series = Series(self._to_dict())
|
228
|
+
return series.__str__()
|
229
|
+
|
230
|
+
def __repr__(self) -> str:
|
231
|
+
"""
|
232
|
+
Returns a string representation of the rule using a pandas Series.
|
233
|
+
|
234
|
+
Returns:
|
235
|
+
str: A string representation of the rule.
|
236
|
+
"""
|
237
|
+
series = Series(self._to_dict())
|
238
|
+
return series.__repr__()
|
@@ -0,0 +1,56 @@
|
|
1
|
+
from typing import Any
|
2
|
+
from lionagi.libs.ln_convert import to_str, strip_lower
|
3
|
+
from lionagi.core.rule.base import Rule
|
4
|
+
|
5
|
+
|
6
|
+
class BooleanRule(Rule):
|
7
|
+
"""
|
8
|
+
Rule for validating that a value is a boolean.
|
9
|
+
|
10
|
+
Attributes:
|
11
|
+
apply_type (str): The type of data to which the rule applies.
|
12
|
+
"""
|
13
|
+
|
14
|
+
fields: list[str] = ["action_required"]
|
15
|
+
|
16
|
+
def __init__(self, apply_type="bool", **kwargs):
|
17
|
+
super().__init__(apply_type=apply_type, **kwargs)
|
18
|
+
|
19
|
+
async def validate(self, value: Any) -> bool:
|
20
|
+
"""
|
21
|
+
Validate that the value is a boolean.
|
22
|
+
|
23
|
+
Args:
|
24
|
+
value (Any): The value to validate.
|
25
|
+
|
26
|
+
Returns:
|
27
|
+
bool: The validated value.
|
28
|
+
|
29
|
+
Raises:
|
30
|
+
ValueError: If the value is not a valid boolean.
|
31
|
+
"""
|
32
|
+
if isinstance(value, bool):
|
33
|
+
return value
|
34
|
+
raise ValueError(f"Invalid boolean value.")
|
35
|
+
|
36
|
+
async def perform_fix(self, value: Any) -> bool:
|
37
|
+
"""
|
38
|
+
Attempt to fix the value by converting it to a boolean.
|
39
|
+
|
40
|
+
Args:
|
41
|
+
value (Any): The value to fix.
|
42
|
+
|
43
|
+
Returns:
|
44
|
+
bool: The fixed value.
|
45
|
+
|
46
|
+
Raises:
|
47
|
+
ValueError: If the value cannot be converted to a boolean.
|
48
|
+
"""
|
49
|
+
value = strip_lower(to_str(value))
|
50
|
+
if value in ["true", "1", "correct", "yes"]:
|
51
|
+
return True
|
52
|
+
|
53
|
+
elif value in ["false", "0", "incorrect", "no", "none", "n/a"]:
|
54
|
+
return False
|
55
|
+
|
56
|
+
raise ValueError(f"Failed to convert {value} into a boolean value")
|
@@ -0,0 +1,47 @@
|
|
1
|
+
from lionagi.libs.ln_parse import StringMatch
|
2
|
+
from lionagi.core.rule.base import Rule
|
3
|
+
|
4
|
+
|
5
|
+
class ChoiceRule(Rule):
|
6
|
+
"""
|
7
|
+
Rule for validating that a value is within a set of predefined choices.
|
8
|
+
|
9
|
+
Attributes:
|
10
|
+
apply_type (str): The type of data to which the rule applies.
|
11
|
+
keys (list): The list of valid choices.
|
12
|
+
"""
|
13
|
+
|
14
|
+
def __init__(self, apply_type="enum", **kwargs):
|
15
|
+
super().__init__(apply_type=apply_type, **kwargs)
|
16
|
+
self.keys = self.validation_kwargs.get("keys", None)
|
17
|
+
|
18
|
+
async def validate(self, value: str, *args, **kwargs) -> str:
|
19
|
+
"""
|
20
|
+
Validate that the value is within the set of predefined choices.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
value (str): The value to validate.
|
24
|
+
*args: Additional arguments.
|
25
|
+
**kwargs: Additional keyword arguments.
|
26
|
+
|
27
|
+
Returns:
|
28
|
+
str: The validated value.
|
29
|
+
|
30
|
+
Raises:
|
31
|
+
ValueError: If the value is not in the set of choices.
|
32
|
+
"""
|
33
|
+
if not value in self.keys:
|
34
|
+
raise ValueError(f"{value} is not in chocies {self.keys}")
|
35
|
+
return value
|
36
|
+
|
37
|
+
async def perform_fix(self, value):
|
38
|
+
"""
|
39
|
+
Suggest a fix for a value that is not within the set of predefined choices.
|
40
|
+
|
41
|
+
Args:
|
42
|
+
value (str): The value to suggest a fix for.
|
43
|
+
|
44
|
+
Returns:
|
45
|
+
str: The most similar value from the set of predefined choices.
|
46
|
+
"""
|
47
|
+
return StringMatch.choose_most_similar(value, self.keys)
|
@@ -0,0 +1,96 @@
|
|
1
|
+
"""
|
2
|
+
Copyright 2024 HaiyangLi
|
3
|
+
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
you may not use this file except in compliance with the License.
|
6
|
+
You may obtain a copy of the License at
|
7
|
+
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
See the License for the specific language governing permissions and
|
14
|
+
limitations under the License.
|
15
|
+
"""
|
16
|
+
|
17
|
+
from typing import Any
|
18
|
+
from collections.abc import Mapping
|
19
|
+
from lionagi.libs.ln_convert import to_dict
|
20
|
+
from lionagi.libs import StringMatch, ParseUtil
|
21
|
+
|
22
|
+
from lionagi.core.rule.choice import ChoiceRule
|
23
|
+
|
24
|
+
|
25
|
+
class MappingRule(ChoiceRule):
|
26
|
+
"""
|
27
|
+
Rule for validating that a value is a mapping (dictionary) with specific keys.
|
28
|
+
|
29
|
+
Attributes:
|
30
|
+
apply_type (str): The type of data to which the rule applies.
|
31
|
+
"""
|
32
|
+
|
33
|
+
def __init__(self, apply_type="dict", **kwargs):
|
34
|
+
super().__init__(apply_type=apply_type, **kwargs)
|
35
|
+
|
36
|
+
async def validate(self, value: Any, *args, **kwargs) -> Any:
|
37
|
+
"""
|
38
|
+
Validate that the value is a mapping with specific keys.
|
39
|
+
|
40
|
+
Args:
|
41
|
+
value (Any): The value to validate.
|
42
|
+
*args: Additional arguments.
|
43
|
+
**kwargs: Additional keyword arguments.
|
44
|
+
|
45
|
+
Returns:
|
46
|
+
Any: The validated value.
|
47
|
+
|
48
|
+
Raises:
|
49
|
+
ValueError: If the value is not a valid mapping or has incorrect keys.
|
50
|
+
"""
|
51
|
+
if not isinstance(value, Mapping):
|
52
|
+
raise ValueError("Invalid mapping field type.")
|
53
|
+
|
54
|
+
if self.keys:
|
55
|
+
if (keys := set(value.keys())) != set(self.keys):
|
56
|
+
raise ValueError(
|
57
|
+
f"Invalid mapping keys. Current keys {[i for i in keys]} != {self.keys}"
|
58
|
+
)
|
59
|
+
return value
|
60
|
+
|
61
|
+
async def perform_fix(self, value: Any, *args, **kwargs) -> Any:
|
62
|
+
"""
|
63
|
+
Attempt to fix the value by converting it to a dict and validating its keys.
|
64
|
+
|
65
|
+
Args:
|
66
|
+
value (Any): The value to fix.
|
67
|
+
*args: Additional arguments.
|
68
|
+
**kwargs: Additional keyword arguments.
|
69
|
+
|
70
|
+
Returns:
|
71
|
+
Any: The fixed value.
|
72
|
+
|
73
|
+
Raises:
|
74
|
+
ValueError: If the value cannot be fixed.
|
75
|
+
"""
|
76
|
+
if not isinstance(value, dict):
|
77
|
+
try:
|
78
|
+
value = to_dict(value)
|
79
|
+
except Exception as e:
|
80
|
+
raise ValueError("Invalid dict field type.") from e
|
81
|
+
|
82
|
+
if self.keys:
|
83
|
+
check_keys = set(value.keys())
|
84
|
+
if check_keys != set(self.keys):
|
85
|
+
try:
|
86
|
+
return StringMatch.force_validate_dict(value, keys=self.keys)
|
87
|
+
except Exception as e:
|
88
|
+
raise ValueError("Invalid dict keys.") from e
|
89
|
+
|
90
|
+
else:
|
91
|
+
try:
|
92
|
+
return ParseUtil.fuzzy_parse_json(value)
|
93
|
+
except Exception as e:
|
94
|
+
raise ValueError("Invalid dict keys.") from e
|
95
|
+
|
96
|
+
return value
|
@@ -0,0 +1,71 @@
|
|
1
|
+
from typing import Any
|
2
|
+
from lionagi.libs.ln_convert import to_num
|
3
|
+
from lionagi.core.rule.base import Rule
|
4
|
+
|
5
|
+
|
6
|
+
class NumberRule(Rule):
|
7
|
+
"""
|
8
|
+
Rule for validating that a value is a number within specified bounds.
|
9
|
+
|
10
|
+
Attributes:
|
11
|
+
apply_type (str): The type of data to which the rule applies.
|
12
|
+
upper_bound (int | float | None): The upper bound for the value.
|
13
|
+
lower_bound (int | float | None): The lower bound for the value.
|
14
|
+
num_type (Type[int | float]): The type of number (int or float).
|
15
|
+
precision (int | None): The precision for floating point numbers.
|
16
|
+
"""
|
17
|
+
|
18
|
+
fields: list[str] = ["confidence_score", "score"]
|
19
|
+
|
20
|
+
def __init__(self, apply_type="int, float", **kwargs):
|
21
|
+
super().__init__(apply_type=apply_type, **kwargs)
|
22
|
+
self.upper_bound = self.validation_kwargs.get("upper_bound")
|
23
|
+
self.lower_bound = self.validation_kwargs.get("lower_bound")
|
24
|
+
self.num_type = self.validation_kwargs.get("num_type", float)
|
25
|
+
self.precision = self.validation_kwargs.get("precision")
|
26
|
+
|
27
|
+
async def validate(self, value: Any) -> Any:
|
28
|
+
"""
|
29
|
+
Validate that the value is a number.
|
30
|
+
|
31
|
+
Args:
|
32
|
+
value (Any): The value to validate.
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
Any: The validated value.
|
36
|
+
|
37
|
+
Raises:
|
38
|
+
ValueError: If the value is not a valid number.
|
39
|
+
"""
|
40
|
+
if isinstance(value, (int, float)):
|
41
|
+
return value
|
42
|
+
raise ValueError(f"Invalid number field: {value}")
|
43
|
+
|
44
|
+
async def perform_fix(self, value: Any) -> Any:
|
45
|
+
"""
|
46
|
+
Attempt to fix the value by converting it to a number.
|
47
|
+
|
48
|
+
Args:
|
49
|
+
value (Any): The value to fix.
|
50
|
+
|
51
|
+
Returns:
|
52
|
+
Any: The fixed value.
|
53
|
+
|
54
|
+
Raises:
|
55
|
+
ValueError: If the value cannot be converted to a number.
|
56
|
+
"""
|
57
|
+
if isinstance(value, (int, float)):
|
58
|
+
return value
|
59
|
+
|
60
|
+
value = to_num(
|
61
|
+
value,
|
62
|
+
**{
|
63
|
+
k: v
|
64
|
+
for k, v in self.validation_kwargs.items()
|
65
|
+
if k in ["num_type", "precision", "upper_bound", "lower_bound"]
|
66
|
+
},
|
67
|
+
)
|
68
|
+
|
69
|
+
if isinstance(value, (int, float)):
|
70
|
+
return value
|
71
|
+
raise ValueError(f"Failed to convert {value} into a numeric value")
|
@@ -0,0 +1,109 @@
|
|
1
|
+
from lionagi.libs.ln_func_call import lcall
|
2
|
+
from lionagi.core.rule.base import Rule
|
3
|
+
|
4
|
+
|
5
|
+
"""
|
6
|
+
rule config schema
|
7
|
+
|
8
|
+
{
|
9
|
+
rule_name: {
|
10
|
+
"fields: [],
|
11
|
+
"config": {},
|
12
|
+
...
|
13
|
+
}
|
14
|
+
}
|
15
|
+
"""
|
16
|
+
|
17
|
+
|
18
|
+
class RuleBook:
|
19
|
+
|
20
|
+
def __init__(
|
21
|
+
self,
|
22
|
+
rules: dict[str, Rule] | list[Rule] = None,
|
23
|
+
ruleorder: list[str] = None,
|
24
|
+
rule_config: dict[str, dict] = None,
|
25
|
+
):
|
26
|
+
self.rules = rules
|
27
|
+
self.ruleorder = ruleorder
|
28
|
+
self.rule_config = rule_config or {k: {} for k in self.ruleorder}
|
29
|
+
|
30
|
+
@property
|
31
|
+
def _all_applied_log(self):
|
32
|
+
"""return all applied logs from all rules in the rulebook"""
|
33
|
+
return lcall(self.rules.values(), lambda x: x.applied_log, flatten=True)
|
34
|
+
|
35
|
+
@property
|
36
|
+
def _all_invoked_log(self):
|
37
|
+
"""return all invoked logs from all rules in the rulebook"""
|
38
|
+
return lcall(self.rules.values(), lambda x: x.invoked_log, flatten=True)
|
39
|
+
|
40
|
+
def __getitem__(self, key: str) -> Rule:
|
41
|
+
return self.rules[key]
|
42
|
+
|
43
|
+
# def add_rule(self, rule_name: str, rule: Rule, config: dict = None):
|
44
|
+
# if rule_name in self.rules:
|
45
|
+
# raise ValueError(f"Rule '{rule_name}' already exists.")
|
46
|
+
# self.rules[rule_name] = rule
|
47
|
+
# self.ruleorder.append(rule_name)
|
48
|
+
# self.rule_config[rule_name] = config or {}
|
49
|
+
|
50
|
+
# def remove_rule(self, rule_name: str):
|
51
|
+
# if rule_name not in self.rules:
|
52
|
+
# raise ValueError(f"Rule '{rule_name}' does not exist.")
|
53
|
+
# del self.rules[rule_name]
|
54
|
+
# self.ruleorder.remove(rule_name)
|
55
|
+
# del self.rule_config[rule_name]
|
56
|
+
|
57
|
+
# def update_rule_config(self, rule_name: str, config: dict):
|
58
|
+
# if rule_name not in self.rules:
|
59
|
+
# raise ValueError(f"Rule '{rule_name}' does not exist.")
|
60
|
+
# self.rule_config[rule_name] = config
|
61
|
+
|
62
|
+
# def list_rules(self) -> list[str]:
|
63
|
+
# return self.ruleorder
|
64
|
+
|
65
|
+
# def get_rule_details(self, rule_name: str) -> dict:
|
66
|
+
# if rule_name not in self.rules:
|
67
|
+
# raise ValueError(f"Rule '{rule_name}' does not exist.")
|
68
|
+
# return {
|
69
|
+
# "rule": self.rules[rule_name],
|
70
|
+
# "config": self.rule_config[rule_name]
|
71
|
+
# }
|
72
|
+
|
73
|
+
# async def validate_data(self, data: Any) -> bool:
|
74
|
+
# for rule in self.rules.values():
|
75
|
+
# if not await rule.validate(data):
|
76
|
+
# return False
|
77
|
+
# return True
|
78
|
+
|
79
|
+
# def export_rulebook(self, filepath: str):
|
80
|
+
# import json
|
81
|
+
# with open(filepath, 'w') as f:
|
82
|
+
# json.dump({
|
83
|
+
# "rules": list(self.rules.keys()),
|
84
|
+
# "ruleorder": self.ruleorder,
|
85
|
+
# "rule_config": self.rule_config
|
86
|
+
# }, f)
|
87
|
+
|
88
|
+
# @classmethod
|
89
|
+
# def import_rulebook(cls, filepath: str) -> 'RuleBook':
|
90
|
+
# import json
|
91
|
+
# with open(filepath, 'r') as f:
|
92
|
+
# config = json.load(f)
|
93
|
+
# rules = {name: Rule() for name in config["rules"]}
|
94
|
+
# return cls(rules=rules, ruleorder=config["ruleorder"], rule_config=config["rule_config"])
|
95
|
+
|
96
|
+
# def enable_rule(self, rule_name: str, enable: bool = True):
|
97
|
+
# if rule_name not in self.rules:
|
98
|
+
# raise ValueError(f"Rule '{rule_name}' does not exist.")
|
99
|
+
# self.rules[rule_name].enabled = enable
|
100
|
+
|
101
|
+
# def log_rule_application(self, rule_name: str, data: Any):
|
102
|
+
# if rule_name not in self.rules:
|
103
|
+
# raise ValueError(f"Rule '{rule_name}' does not exist.")
|
104
|
+
# log_entry = {
|
105
|
+
# "rule": rule_name,
|
106
|
+
# "data": data,
|
107
|
+
# "timestamp": SysUtil.get_timestamp()
|
108
|
+
# }
|
109
|
+
# # Append log_entry to a log file or a logging system
|
@@ -0,0 +1,52 @@
|
|
1
|
+
from lionagi.libs.ln_convert import to_str
|
2
|
+
from lionagi.core.rule.base import Rule
|
3
|
+
|
4
|
+
|
5
|
+
class StringRule(Rule):
|
6
|
+
"""
|
7
|
+
Rule for validating and converting string values.
|
8
|
+
|
9
|
+
Attributes:
|
10
|
+
fields (list[str]): The list of fields to which the rule applies.
|
11
|
+
apply_type (str): The type of data to which the rule applies.
|
12
|
+
"""
|
13
|
+
|
14
|
+
fields: list[str] = ["reason", "prediction", "answer"]
|
15
|
+
|
16
|
+
def __init__(self, apply_type="str", **kwargs):
|
17
|
+
super().__init__(apply_type=apply_type, **kwargs)
|
18
|
+
|
19
|
+
async def validate(self, value):
|
20
|
+
"""
|
21
|
+
Validate that the value is a string.
|
22
|
+
|
23
|
+
Args:
|
24
|
+
value: The value to validate.
|
25
|
+
|
26
|
+
Returns:
|
27
|
+
str: The validated string value.
|
28
|
+
|
29
|
+
Raises:
|
30
|
+
ValueError: If the value is not a string or is an empty string.
|
31
|
+
"""
|
32
|
+
if isinstance(value, str) or value == "":
|
33
|
+
return value
|
34
|
+
raise ValueError(f"Invalid string field type.")
|
35
|
+
|
36
|
+
async def perform_fix(self, value):
|
37
|
+
"""
|
38
|
+
Attempt to convert a value to a string.
|
39
|
+
|
40
|
+
Args:
|
41
|
+
value: The value to convert to a string.
|
42
|
+
|
43
|
+
Returns:
|
44
|
+
str: The value converted to a string.
|
45
|
+
|
46
|
+
Raises:
|
47
|
+
ValueError: If the value cannot be converted to a string.
|
48
|
+
"""
|
49
|
+
try:
|
50
|
+
return to_str(value, **self.validation_kwargs)
|
51
|
+
except Exception as e:
|
52
|
+
raise ValueError(f"Failed to convert {value} into a string value") from e
|