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,231 @@
|
|
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
|
+
"""
|
18
|
+
This module extends the BaseForm class to implement the Form class, which
|
19
|
+
dynamically manages form operations based on specific assignments. It provides
|
20
|
+
functionalities for initializing fields, filling forms with data, and
|
21
|
+
validating the readiness of forms for further processing.
|
22
|
+
"""
|
23
|
+
|
24
|
+
from typing import Dict, Any
|
25
|
+
from lionagi.libs.ln_convert import to_readable_dict
|
26
|
+
from lionagi.core.collections.abc import SYSTEM_FIELDS
|
27
|
+
from lionagi.core.report.util import get_input_output_fields
|
28
|
+
from lionagi.core.report.base import BaseForm
|
29
|
+
|
30
|
+
|
31
|
+
class Form(BaseForm):
|
32
|
+
"""
|
33
|
+
A specialized implementation of BaseForm designed to manage form fields
|
34
|
+
dynamically based on specified assignments. Supports initialization and
|
35
|
+
management of input and requested fields, handles form filling operations,
|
36
|
+
and ensures forms are properly configured for use.
|
37
|
+
|
38
|
+
Attributes:
|
39
|
+
input_fields (List[str]): Fields required to carry out the objective of the form.
|
40
|
+
requested_fields (List[str]): Fields requested to be filled by the user.
|
41
|
+
"""
|
42
|
+
|
43
|
+
def __init__(self, **kwargs):
|
44
|
+
"""
|
45
|
+
Initializes a new instance of the Form, setting up input and requested
|
46
|
+
fields based on the form's assignment.
|
47
|
+
"""
|
48
|
+
super().__init__(**kwargs)
|
49
|
+
self.input_fields, self.requested_fields = get_input_output_fields(
|
50
|
+
self.assignment
|
51
|
+
)
|
52
|
+
|
53
|
+
for i in self.input_fields:
|
54
|
+
self.append_to_input(i)
|
55
|
+
|
56
|
+
for i in self.input_fields + self.requested_fields:
|
57
|
+
if i not in self._all_fields:
|
58
|
+
self._add_field(i, value=None)
|
59
|
+
|
60
|
+
def append_to_request(self, field: str, value=None):
|
61
|
+
"""
|
62
|
+
Appends a field to the requested fields.
|
63
|
+
|
64
|
+
Args:
|
65
|
+
field (str): The name of the field to be requested.
|
66
|
+
value (optional): The value to be assigned to the field. Defaults to None.
|
67
|
+
"""
|
68
|
+
if "," in field:
|
69
|
+
field = field.split(",")
|
70
|
+
if not isinstance(field, list):
|
71
|
+
field = [field]
|
72
|
+
|
73
|
+
for i in field:
|
74
|
+
i = i.strip()
|
75
|
+
if i not in self._all_fields:
|
76
|
+
self._add_field(i, value=value)
|
77
|
+
|
78
|
+
if i not in self.requested_fields:
|
79
|
+
self.requested_fields.append(i)
|
80
|
+
self.validation_kwargs[i] = self._get_field_attr(
|
81
|
+
i, "validation_kwargs", {}
|
82
|
+
)
|
83
|
+
|
84
|
+
def append_to_input(self, field: str, value=None):
|
85
|
+
"""
|
86
|
+
Appends a field to the input fields.
|
87
|
+
|
88
|
+
Args:
|
89
|
+
field (str): The name of the field to be added to input.
|
90
|
+
value (optional): The value to be assigned to the field. Defaults to None.
|
91
|
+
"""
|
92
|
+
if "," in field:
|
93
|
+
field = field.split(",")
|
94
|
+
if not isinstance(field, list):
|
95
|
+
field = [field]
|
96
|
+
|
97
|
+
for i in field:
|
98
|
+
i = i.strip()
|
99
|
+
if i not in self._all_fields:
|
100
|
+
self._add_field(i, value=value)
|
101
|
+
|
102
|
+
if i not in self.input_fields:
|
103
|
+
self.input_fields.append(i)
|
104
|
+
self.validation_kwargs[i] = self._get_field_attr(
|
105
|
+
i, "validation_kwargs", {}
|
106
|
+
)
|
107
|
+
|
108
|
+
@property
|
109
|
+
def work_fields(self) -> Dict[str, Any]:
|
110
|
+
"""
|
111
|
+
Retrieves a dictionary of the fields relevant to the current task,
|
112
|
+
excluding any SYSTEM_FIELDS and including only the input and requested
|
113
|
+
fields.
|
114
|
+
|
115
|
+
Returns:
|
116
|
+
Dict[str, Any]: The relevant fields for the current task.
|
117
|
+
"""
|
118
|
+
dict_ = self.to_dict()
|
119
|
+
return {
|
120
|
+
k: v
|
121
|
+
for k, v in dict_.items()
|
122
|
+
if k not in SYSTEM_FIELDS and k in self.input_fields + self.requested_fields
|
123
|
+
}
|
124
|
+
|
125
|
+
def fill(self, form: "Form" = None, strict: bool = True, **kwargs) -> None:
|
126
|
+
"""
|
127
|
+
Fills the form from another form instance or provided keyword arguments.
|
128
|
+
Raises an error if the form is already filled.
|
129
|
+
|
130
|
+
Args:
|
131
|
+
form (Form, optional): The form to fill from.
|
132
|
+
strict (bool, optional): Whether to enforce strict filling. Defaults to True.
|
133
|
+
**kwargs: Additional fields to fill.
|
134
|
+
"""
|
135
|
+
if self.filled:
|
136
|
+
if strict:
|
137
|
+
raise ValueError("Form is filled, cannot be worked on again")
|
138
|
+
|
139
|
+
all_fields = self._get_all_fields(form, **kwargs)
|
140
|
+
|
141
|
+
for k, v in all_fields.items():
|
142
|
+
if (
|
143
|
+
k in self.work_fields
|
144
|
+
and v is not None
|
145
|
+
and getattr(self, k, None) is None
|
146
|
+
):
|
147
|
+
setattr(self, k, v)
|
148
|
+
|
149
|
+
def is_workable(self) -> bool:
|
150
|
+
"""
|
151
|
+
Determines if the form is ready for processing. Checks if all required
|
152
|
+
fields are filled and raises an error if the form is already filled or
|
153
|
+
if any required field is missing.
|
154
|
+
|
155
|
+
Returns:
|
156
|
+
bool: True if the form is workable, otherwise raises ValueError.
|
157
|
+
"""
|
158
|
+
if self.filled:
|
159
|
+
raise ValueError("Form is already filled, cannot be worked on again")
|
160
|
+
|
161
|
+
for i in self.input_fields:
|
162
|
+
if not getattr(self, i, None):
|
163
|
+
raise ValueError(f"Required field {i} is not provided")
|
164
|
+
|
165
|
+
return True
|
166
|
+
|
167
|
+
@property
|
168
|
+
def _instruction_context(self) -> str:
|
169
|
+
"""
|
170
|
+
Generates a detailed description of the input fields, including their
|
171
|
+
current values and descriptions.
|
172
|
+
|
173
|
+
Returns:
|
174
|
+
str: A detailed description of the input fields.
|
175
|
+
"""
|
176
|
+
return "".join(
|
177
|
+
f"""
|
178
|
+
## input: {i}:
|
179
|
+
- description: {getattr(self._all_fields[i], "description", "N/A")}
|
180
|
+
- value: {str(getattr(self, self.input_fields[idx]))}
|
181
|
+
"""
|
182
|
+
for idx, i in enumerate(self.input_fields)
|
183
|
+
)
|
184
|
+
|
185
|
+
@property
|
186
|
+
def _instruction_prompt(self) -> str:
|
187
|
+
return f"""
|
188
|
+
## Task Instructions
|
189
|
+
Please follow prompts to complete the task:
|
190
|
+
1. Your task is: {self.task}
|
191
|
+
2. The provided input fields are: {', '.join(self.input_fields)}
|
192
|
+
3. The requested output fields are: {', '.join(self.requested_fields)}
|
193
|
+
4. Provide your response in the specified JSON format.
|
194
|
+
"""
|
195
|
+
|
196
|
+
@property
|
197
|
+
def _instruction_requested_fields(self) -> Dict[str, str]:
|
198
|
+
"""
|
199
|
+
Provides a dictionary mapping requested field names to their
|
200
|
+
descriptions.
|
201
|
+
|
202
|
+
Returns:
|
203
|
+
Dict[str, str]: A dictionary of requested field descriptions.
|
204
|
+
"""
|
205
|
+
return {
|
206
|
+
i: getattr(self._all_fields[i], "description", "N/A")
|
207
|
+
for i in self.requested_fields
|
208
|
+
}
|
209
|
+
|
210
|
+
def display(self, fields=None):
|
211
|
+
"""
|
212
|
+
Displays the form fields using IPython display.
|
213
|
+
|
214
|
+
Args:
|
215
|
+
fields (optional): Specific fields to display. Defaults to None.
|
216
|
+
"""
|
217
|
+
from IPython.display import display, Markdown
|
218
|
+
|
219
|
+
fields = fields or self.work_fields
|
220
|
+
|
221
|
+
if "answer" in fields:
|
222
|
+
answer = fields.pop("answer")
|
223
|
+
fields["answer"] = answer
|
224
|
+
|
225
|
+
for k, v in fields.items():
|
226
|
+
if isinstance(v, dict):
|
227
|
+
v = to_readable_dict(v)
|
228
|
+
if len(str(v)) > 50:
|
229
|
+
display(Markdown(f"**{k}**: \n {v}"))
|
230
|
+
else:
|
231
|
+
display(Markdown(f"**{k}**: {v}"))
|
@@ -0,0 +1,166 @@
|
|
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
|
+
"""
|
18
|
+
This module introduces the Report class, an extension of the BaseForm class
|
19
|
+
designed to manage and synchronize a collection of Form instances based on
|
20
|
+
specific assignments. The Report class handles the creation and updating of
|
21
|
+
forms, ensuring each is properly configured according to the report's
|
22
|
+
requirements.
|
23
|
+
"""
|
24
|
+
|
25
|
+
from typing import Any, Type
|
26
|
+
from lionagi.core.collections.abc import Field
|
27
|
+
from lionagi.core.collections import Pile, pile
|
28
|
+
from lionagi.core.report.util import get_input_output_fields
|
29
|
+
from lionagi.core.report.base import BaseForm
|
30
|
+
from lionagi.core.report.form import Form
|
31
|
+
|
32
|
+
|
33
|
+
class Report(BaseForm):
|
34
|
+
"""
|
35
|
+
Extends BaseForm to handle a collection of Form instances based on specific
|
36
|
+
assignments, managing a pile of forms and ensuring synchronization and
|
37
|
+
proper configuration.
|
38
|
+
"""
|
39
|
+
|
40
|
+
forms: Pile[Form] = Field(
|
41
|
+
None,
|
42
|
+
description="A pile of forms related to the report.",
|
43
|
+
)
|
44
|
+
|
45
|
+
form_assignments: list = Field(
|
46
|
+
[],
|
47
|
+
description="assignment for the report",
|
48
|
+
examples=[["a, b -> c", "a -> e", "b -> f", "c -> g", "e, f, g -> h"]],
|
49
|
+
)
|
50
|
+
|
51
|
+
form_template: Type[Form] = Field(
|
52
|
+
Form, description="The template for the forms in the report."
|
53
|
+
)
|
54
|
+
|
55
|
+
def __init__(self, **kwargs):
|
56
|
+
"""
|
57
|
+
Initializes the Report with input and requested fields based on the
|
58
|
+
report's assignment, creating forms dynamically from provided assignments.
|
59
|
+
"""
|
60
|
+
super().__init__(**kwargs)
|
61
|
+
self.input_fields, self.requested_fields = get_input_output_fields(
|
62
|
+
self.assignment
|
63
|
+
)
|
64
|
+
|
65
|
+
# if assignments is not provided, set it to report assignment
|
66
|
+
if self.form_assignments == []:
|
67
|
+
self.form_assignments.append(self.assignment)
|
68
|
+
|
69
|
+
# create forms
|
70
|
+
self.forms = pile(
|
71
|
+
[self.form_template(assignment=i) for i in self.form_assignments],
|
72
|
+
[Form, BaseForm, Report],
|
73
|
+
)
|
74
|
+
|
75
|
+
# Add undeclared fields to report with None values
|
76
|
+
for v in self.forms:
|
77
|
+
for _field in list(v.work_fields.keys()):
|
78
|
+
if _field not in self._all_fields:
|
79
|
+
field_obj = v._all_fields[_field]
|
80
|
+
self._add_field(_field, value=None, field_obj=field_obj)
|
81
|
+
|
82
|
+
# Synchronize fields between report and forms
|
83
|
+
for k, v in self._all_fields.items():
|
84
|
+
if getattr(self, k, None) is not None:
|
85
|
+
for _form in self.forms:
|
86
|
+
if k in _form.work_fields:
|
87
|
+
_form.fill(**{k: getattr(self, k)})
|
88
|
+
|
89
|
+
@property
|
90
|
+
def work_fields(self) -> dict[str, Any]:
|
91
|
+
all_fields = {}
|
92
|
+
for form in self.forms.values():
|
93
|
+
for k, v in form.work_fields.items():
|
94
|
+
if k not in all_fields:
|
95
|
+
all_fields[k] = v
|
96
|
+
return all_fields
|
97
|
+
|
98
|
+
def fill(self, form: Form | list[Form] | dict[Form] = None, strict=True, **kwargs):
|
99
|
+
if self.filled:
|
100
|
+
if strict:
|
101
|
+
raise ValueError("Form is filled, cannot be worked on again")
|
102
|
+
|
103
|
+
# gather all unique valid fields from input form,
|
104
|
+
# kwargs and self workfields data
|
105
|
+
all_fields = self._get_all_fields(form, **kwargs)
|
106
|
+
|
107
|
+
# if there are information in the forms that are not in the report,
|
108
|
+
# add them to the report
|
109
|
+
for k, v in all_fields.items():
|
110
|
+
if k in self.work_fields and getattr(self, k, None) is None:
|
111
|
+
setattr(self, k, v)
|
112
|
+
|
113
|
+
# if there are information in the report that are not in the forms,
|
114
|
+
# add them to the forms
|
115
|
+
for _form in self.forms:
|
116
|
+
for k, v in _form.work_fields.items():
|
117
|
+
_kwargs = {}
|
118
|
+
if v is None and (a := getattr(self, k, None)) is not None:
|
119
|
+
_kwargs[k] = a
|
120
|
+
_form.fill(**_kwargs)
|
121
|
+
|
122
|
+
def is_workable(self) -> bool:
|
123
|
+
"""
|
124
|
+
Checks if the report is ready for processing, ensuring all necessary fields
|
125
|
+
are filled and output fields are unique across forms.
|
126
|
+
|
127
|
+
Returns:
|
128
|
+
bool: True if the report is workable, otherwise raises ValueError.
|
129
|
+
"""
|
130
|
+
if self.filled:
|
131
|
+
raise ValueError("Form is already filled, cannot be worked on again")
|
132
|
+
|
133
|
+
for i in self.input_fields:
|
134
|
+
if not getattr(self, i, None):
|
135
|
+
raise ValueError(f"Required field {i} is not provided")
|
136
|
+
|
137
|
+
# this is the required fields from report's own assignment
|
138
|
+
fields = self.input_fields
|
139
|
+
fields.extend(self.requested_fields)
|
140
|
+
|
141
|
+
# if the report's own assignment is not in the forms, return False
|
142
|
+
for f in fields:
|
143
|
+
if f not in self.work_fields:
|
144
|
+
raise ValueError(f"Field {f} is not in the forms")
|
145
|
+
|
146
|
+
# get all the output fields from all the forms
|
147
|
+
outs = []
|
148
|
+
for form in self.forms.values():
|
149
|
+
outs.extend(form.requested_fields)
|
150
|
+
|
151
|
+
# all output fields should be unique, not a single output field should be
|
152
|
+
# calculated by more than one form
|
153
|
+
if len(outs) != len(set(outs)):
|
154
|
+
raise ValueError("Output fields are not unique")
|
155
|
+
|
156
|
+
return True
|
157
|
+
|
158
|
+
def next_forms(self) -> Pile[Form]:
|
159
|
+
"""
|
160
|
+
Returns a pile of workable forms based on current form states within the report.
|
161
|
+
|
162
|
+
Returns:
|
163
|
+
Pile[Form]: A pile of workable forms or None if there are no workable forms.
|
164
|
+
"""
|
165
|
+
a = [i for i in self.forms if i.workable]
|
166
|
+
return pile(a, Form) if len(a) > 0 else None
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from lionagi.libs.ln_convert import strip_lower
|
2
|
+
|
3
|
+
|
4
|
+
def get_input_output_fields(str_: str) -> list[list[str]]:
|
5
|
+
"""
|
6
|
+
Parses an assignment string to extract input and output fields.
|
7
|
+
|
8
|
+
Args:
|
9
|
+
str_ (str): The assignment string in the format 'inputs -> outputs'.
|
10
|
+
|
11
|
+
Returns:
|
12
|
+
list[list[str]]: A list containing two lists - one for input fields and one for requested fields.
|
13
|
+
|
14
|
+
Raises:
|
15
|
+
ValueError: If the assignment string is None or if it does not contain '->' indicating invalid format.
|
16
|
+
"""
|
17
|
+
if str_ is None:
|
18
|
+
return [], []
|
19
|
+
|
20
|
+
if "->" not in str_:
|
21
|
+
raise ValueError("Invalid assignment format. Expected 'inputs -> outputs'.")
|
22
|
+
|
23
|
+
inputs, outputs = str_.split("->")
|
24
|
+
|
25
|
+
input_fields = [strip_lower(i) for i in inputs.split(",")]
|
26
|
+
requested_fields = [strip_lower(o) for o in outputs.split(",")]
|
27
|
+
|
28
|
+
return input_fields, requested_fields
|
File without changes
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
from .choice import ChoiceRule
|
3
|
+
from .mapping import MappingRule
|
4
|
+
from .number import NumberRule
|
5
|
+
from .boolean import BooleanRule
|
6
|
+
from .string import StringRule
|
7
|
+
from .action import ActionRequestRule
|
8
|
+
|
9
|
+
|
10
|
+
class DEFAULT_RULES(Enum):
|
11
|
+
CHOICE = ChoiceRule
|
12
|
+
MAPPING = MappingRule
|
13
|
+
NUMBER = NumberRule
|
14
|
+
BOOL = BooleanRule
|
15
|
+
STR = StringRule
|
16
|
+
ACTION = ActionRequestRule
|
@@ -0,0 +1,99 @@
|
|
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 enum import Enum
|
18
|
+
from lionagi.core.collections.abc import ActionError
|
19
|
+
from lionagi.libs import ParseUtil
|
20
|
+
from lionagi.libs.ln_convert import to_list, to_dict
|
21
|
+
from lionagi.core.rule.mapping import MappingRule
|
22
|
+
|
23
|
+
|
24
|
+
class ActionRequestKeys(Enum):
|
25
|
+
FUNCTION = "function"
|
26
|
+
ARGUMENTS = "arguments"
|
27
|
+
|
28
|
+
|
29
|
+
class ActionRequestRule(MappingRule):
|
30
|
+
"""
|
31
|
+
Rule for validating and fixing action requests.
|
32
|
+
|
33
|
+
Inherits from `MappingRule` and provides specific validation and fix logic
|
34
|
+
for action requests.
|
35
|
+
|
36
|
+
Attributes:
|
37
|
+
discard (bool): Indicates whether to discard invalid action requests.
|
38
|
+
"""
|
39
|
+
|
40
|
+
def __init__(self, apply_type="actionrequest", discard=True, **kwargs):
|
41
|
+
"""
|
42
|
+
Initializes the ActionRequestRule.
|
43
|
+
|
44
|
+
Args:
|
45
|
+
apply_type (str): The type of data to which the rule applies.
|
46
|
+
discard (bool, optional): Indicates whether to discard invalid action requests.
|
47
|
+
**kwargs: Additional keyword arguments for initialization.
|
48
|
+
"""
|
49
|
+
super().__init__(
|
50
|
+
apply_type=apply_type, keys=ActionRequestKeys, fix=True, **kwargs
|
51
|
+
)
|
52
|
+
self.discard = discard or self.validation_kwargs.get("discard", False)
|
53
|
+
|
54
|
+
async def validate(self, value):
|
55
|
+
"""
|
56
|
+
Validates the action request.
|
57
|
+
|
58
|
+
Args:
|
59
|
+
value (Any): The value of the action request.
|
60
|
+
|
61
|
+
Returns:
|
62
|
+
Any: The validated action request.
|
63
|
+
|
64
|
+
Raises:
|
65
|
+
ActionError: If the action request is invalid.
|
66
|
+
"""
|
67
|
+
if isinstance(value, dict) and list(value.keys()) >= ["function", "arguments"]:
|
68
|
+
return value
|
69
|
+
raise ActionError(f"Invalid action request: {value}")
|
70
|
+
|
71
|
+
async def perform_fix(self, value):
|
72
|
+
"""
|
73
|
+
Attempts to fix an invalid action request.
|
74
|
+
|
75
|
+
Args:
|
76
|
+
value (Any): The value of the action request to fix.
|
77
|
+
|
78
|
+
Returns:
|
79
|
+
Any: The fixed action request.
|
80
|
+
|
81
|
+
Raises:
|
82
|
+
ActionError: If the action request cannot be fixed.
|
83
|
+
"""
|
84
|
+
corrected = []
|
85
|
+
if isinstance(value, str):
|
86
|
+
value = ParseUtil.fuzzy_parse_json(value)
|
87
|
+
|
88
|
+
try:
|
89
|
+
value = to_list(value)
|
90
|
+
for i in value:
|
91
|
+
i = to_dict(i)
|
92
|
+
if list(i.keys()) >= ["function", "arguments"]:
|
93
|
+
corrected.append(i)
|
94
|
+
elif not self.discard:
|
95
|
+
raise ActionError(f"Invalid action request: {i}")
|
96
|
+
except Exception as e:
|
97
|
+
raise ActionError(f"Invalid action field: ") from e
|
98
|
+
|
99
|
+
return corrected
|