lionagi 0.1.2__py3-none-any.whl → 0.2.0__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 +60 -5
- lionagi/core/__init__.py +0 -25
- 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/base_agent.py +27 -13
- 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/{generic/data_logger.py → collections/_logger.py} +69 -55
- 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/{execute/branch_executor.py → engine/branch_engine.py} +134 -97
- lionagi/core/{execute/instruction_map_executor.py → engine/instruction_map_engine.py} +80 -55
- lionagi/{experimental/directive/evaluator → core/engine}/script_engine.py +17 -1
- lionagi/core/executor/base_executor.py +90 -0
- lionagi/core/{execute/structure_executor.py → executor/graph_executor.py} +62 -66
- lionagi/core/{execute → executor}/neo4j_executor.py +70 -67
- lionagi/core/generic/__init__.py +3 -33
- lionagi/core/generic/edge.py +29 -79
- 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 +156 -221
- lionagi/core/generic/tree.py +48 -0
- lionagi/core/generic/tree_node.py +79 -0
- lionagi/core/mail/__init__.py +12 -0
- lionagi/core/mail/mail.py +25 -0
- lionagi/core/mail/mail_manager.py +139 -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/_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/branch.py +431 -0
- lionagi/core/session/directive_mixin.py +287 -0
- lionagi/core/session/session.py +229 -903
- 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/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/validator.py +364 -0
- lionagi/core/work/work.py +74 -0
- lionagi/core/work/work_function.py +92 -0
- lionagi/core/work/work_queue.py +81 -0
- lionagi/core/work/worker.py +195 -0
- lionagi/core/work/worklog.py +124 -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/base_parser.py +69 -2
- lionagi/experimental/directive/{template_ → template}/base_template.py +17 -1
- lionagi/{libs/ln_tokenizer.py → experimental/directive/tokenizer.py} +16 -0
- lionagi/experimental/{directive/evaluator → evaluator}/ast_evaluator.py +16 -0
- lionagi/experimental/{directive/evaluator → evaluator}/base_evaluator.py +16 -0
- lionagi/experimental/knowledge/base.py +10 -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/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/chunker/chunk.py +161 -24
- lionagi/integrations/config/oai_configs.py +34 -3
- lionagi/integrations/config/openrouter_configs.py +14 -2
- lionagi/integrations/loader/load.py +122 -21
- lionagi/integrations/loader/load_util.py +6 -77
- 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 +6 -5
- 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 -3
- lionagi/integrations/storage/neo4j.py +52 -60
- lionagi/integrations/storage/storage_util.py +44 -46
- lionagi/integrations/storage/structure_excel.py +43 -26
- lionagi/integrations/storage/to_excel.py +11 -4
- lionagi/libs/__init__.py +22 -1
- lionagi/libs/ln_api.py +75 -20
- 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_nested.py +26 -11
- lionagi/libs/ln_parse.py +82 -23
- lionagi/libs/ln_queue.py +16 -0
- lionagi/libs/ln_tokenize.py +164 -0
- lionagi/libs/ln_validate.py +16 -0
- lionagi/libs/special_tokens.py +172 -0
- lionagi/libs/sys_util.py +95 -24
- lionagi/lions/coder/code_form.py +13 -0
- lionagi/lions/coder/coder.py +50 -3
- lionagi/lions/coder/util.py +30 -25
- lionagi/tests/libs/test_func_call.py +23 -21
- lionagi/tests/libs/test_nested.py +36 -21
- lionagi/tests/libs/test_parse.py +1 -1
- 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/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 -294
- 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.2.0.dist-info/LICENSE +202 -0
- lionagi-0.2.0.dist-info/METADATA +272 -0
- lionagi-0.2.0.dist-info/RECORD +240 -0
- lionagi/core/branch/base.py +0 -653
- lionagi/core/branch/branch.py +0 -474
- lionagi/core/branch/flow_mixin.py +0 -96
- lionagi/core/branch/util.py +0 -323
- lionagi/core/direct/__init__.py +0 -19
- lionagi/core/direct/cot.py +0 -123
- lionagi/core/direct/plan.py +0 -164
- lionagi/core/direct/predict.py +0 -166
- lionagi/core/direct/react.py +0 -171
- lionagi/core/direct/score.py +0 -279
- lionagi/core/direct/select.py +0 -170
- lionagi/core/direct/sentiment.py +0 -1
- lionagi/core/direct/utils.py +0 -110
- lionagi/core/direct/vote.py +0 -64
- lionagi/core/execute/base_executor.py +0 -47
- lionagi/core/flow/baseflow.py +0 -23
- lionagi/core/flow/monoflow/ReAct.py +0 -240
- lionagi/core/flow/monoflow/__init__.py +0 -9
- lionagi/core/flow/monoflow/chat.py +0 -95
- lionagi/core/flow/monoflow/chat_mixin.py +0 -253
- lionagi/core/flow/monoflow/followup.py +0 -215
- lionagi/core/flow/polyflow/__init__.py +0 -1
- lionagi/core/flow/polyflow/chat.py +0 -251
- lionagi/core/form/action_form.py +0 -26
- lionagi/core/form/field_validator.py +0 -287
- lionagi/core/form/form.py +0 -302
- lionagi/core/form/mixin.py +0 -214
- lionagi/core/form/scored_form.py +0 -13
- lionagi/core/generic/action.py +0 -26
- lionagi/core/generic/component.py +0 -532
- lionagi/core/generic/condition.py +0 -46
- lionagi/core/generic/mail.py +0 -90
- lionagi/core/generic/mailbox.py +0 -36
- lionagi/core/generic/relation.py +0 -70
- lionagi/core/generic/signal.py +0 -22
- lionagi/core/generic/structure.py +0 -362
- lionagi/core/generic/transfer.py +0 -20
- lionagi/core/generic/work.py +0 -40
- lionagi/core/graph/graph.py +0 -126
- lionagi/core/graph/tree.py +0 -190
- lionagi/core/mail/schema.py +0 -63
- lionagi/core/messages/schema.py +0 -325
- lionagi/core/tool/__init__.py +0 -5
- lionagi/core/tool/tool.py +0 -28
- lionagi/core/tool/tool_manager.py +0 -283
- lionagi/experimental/report/form.py +0 -64
- lionagi/experimental/report/report.py +0 -138
- lionagi/experimental/report/util.py +0 -47
- lionagi/experimental/tool/function_calling.py +0 -43
- lionagi/experimental/tool/manual.py +0 -66
- lionagi/experimental/tool/schema.py +0 -59
- lionagi/experimental/tool/tool_manager.py +0 -138
- lionagi/experimental/tool/util.py +0 -16
- lionagi/experimental/validator/rule.py +0 -139
- lionagi/experimental/validator/validator.py +0 -56
- lionagi/experimental/work/__init__.py +0 -10
- lionagi/experimental/work/async_queue.py +0 -54
- lionagi/experimental/work/schema.py +0 -73
- lionagi/experimental/work/work_function.py +0 -67
- lionagi/experimental/work/worker.py +0 -56
- lionagi/experimental/work2/form.py +0 -371
- lionagi/experimental/work2/report.py +0 -289
- lionagi/experimental/work2/schema.py +0 -30
- lionagi/experimental/work2/tests.py +0 -72
- lionagi/experimental/work2/work_function.py +0 -89
- lionagi/experimental/work2/worker.py +0 -12
- lionagi/integrations/bridge/llamaindex_/get_index.py +0 -294
- lionagi/tests/test_core/generic/test_component.py +0 -89
- lionagi/tests/test_core/test_base_branch.py +0 -426
- 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 -313
- lionagi/tests/test_core/test_tool_manager.py +0 -95
- lionagi-0.1.2.dist-info/LICENSE +0 -9
- lionagi-0.1.2.dist-info/METADATA +0 -174
- lionagi-0.1.2.dist-info/RECORD +0 -206
- /lionagi/core/{branch → _setting}/__init__.py +0 -0
- /lionagi/core/{execute → agent/eval}/__init__.py +0 -0
- /lionagi/core/{flow → agent/learn}/__init__.py +0 -0
- /lionagi/core/{form → agent/plan}/__init__.py +0 -0
- /lionagi/core/{branch/executable_branch.py → agent/plan/plan.py} +0 -0
- /lionagi/core/{graph → director}/__init__.py +0 -0
- /lionagi/core/{messages → engine}/__init__.py +0 -0
- /lionagi/{experimental/directive/evaluator → core/engine}/sandbox_.py +0 -0
- /lionagi/{experimental/directive/evaluator → core/executor}/__init__.py +0 -0
- /lionagi/{experimental/directive/template_ → core/rule}/__init__.py +0 -0
- /lionagi/{experimental/report → core/unit/template}/__init__.py +0 -0
- /lionagi/{experimental/tool → core/validator}/__init__.py +0 -0
- /lionagi/{experimental/validator → core/work}/__init__.py +0 -0
- /lionagi/experimental/{work2 → compressor}/__init__.py +0 -0
- /lionagi/{core/flow/mono_chat_mixin.py → experimental/directive/template/__init__.py} +0 -0
- /lionagi/experimental/directive/{schema.py → template/schema.py} +0 -0
- /lionagi/experimental/{work2/util.py → evaluator/__init__.py} +0 -0
- /lionagi/experimental/{work2/work.py → knowledge/__init__.py} +0 -0
- /lionagi/{tests/libs/test_async.py → experimental/knowledge/graph.py} +0 -0
- {lionagi-0.1.2.dist-info → lionagi-0.2.0.dist-info}/WHEEL +0 -0
- {lionagi-0.1.2.dist-info → lionagi-0.2.0.dist-info}/top_level.txt +0 -0
@@ -1,371 +0,0 @@
|
|
1
|
-
from typing import Any
|
2
|
-
from pydantic import Field
|
3
|
-
from lionagi.core.generic import BaseComponent
|
4
|
-
from lionagi.libs import validation_funcs, convert
|
5
|
-
from lionagi.experimental.report.util import get_input_output_fields
|
6
|
-
|
7
|
-
|
8
|
-
class Form(BaseComponent):
|
9
|
-
|
10
|
-
form_name: str = Field(
|
11
|
-
default="default_form",
|
12
|
-
)
|
13
|
-
description: Any = Field(default=None)
|
14
|
-
assignment: str = Field(..., examples=["input1, input2 -> output"])
|
15
|
-
instruction: Any = Field(
|
16
|
-
default=None,
|
17
|
-
)
|
18
|
-
input_fields: list[str] = Field(default_factory=list)
|
19
|
-
output_fields: list[str] = Field(default_factory=list)
|
20
|
-
examples: Any = Field(
|
21
|
-
default=None,
|
22
|
-
)
|
23
|
-
fix_input: bool = Field(False, description="whether to fix input")
|
24
|
-
fix_output: bool = Field(True, description="whether to fix output")
|
25
|
-
filled: bool = Field(False, description="whether the form is completed")
|
26
|
-
|
27
|
-
def __init__(self, **kwargs):
|
28
|
-
super().__init__(**kwargs)
|
29
|
-
self.input_fields, self.output_fields = get_input_output_fields(self.assignment)
|
30
|
-
|
31
|
-
for field in self.input_fields:
|
32
|
-
if not hasattr(self, field):
|
33
|
-
setattr(self, field, None) # zero initialization
|
34
|
-
self.process()
|
35
|
-
|
36
|
-
@property
|
37
|
-
def work_fields(self):
|
38
|
-
return self.input_fields + self.output_fields
|
39
|
-
|
40
|
-
@property
|
41
|
-
def is_completed(self):
|
42
|
-
return all(getattr(self, i, None) for i in self.work_fields)
|
43
|
-
|
44
|
-
def process(self, in_=True, out_=None):
|
45
|
-
if not in_ and not out_:
|
46
|
-
raise ValueError("at least one of in_ and out_ must be True")
|
47
|
-
if in_:
|
48
|
-
self._process_input()
|
49
|
-
if out_:
|
50
|
-
self._process_output(out_)
|
51
|
-
|
52
|
-
def _process_input(self):
|
53
|
-
for k in self.input_fields:
|
54
|
-
try:
|
55
|
-
valid_ = self._validate_field(
|
56
|
-
k,
|
57
|
-
getattr(self, k, None),
|
58
|
-
choices=self._get_field_attr(k, "choices", None),
|
59
|
-
keys=self._get_field_attr(k, "keys", None),
|
60
|
-
fix_=self.fix_input,
|
61
|
-
**self._get_field_attr(k, "validation_kwargs", {}),
|
62
|
-
)
|
63
|
-
if not valid_:
|
64
|
-
raise ValueError(f"failed to validate field {k}")
|
65
|
-
except Exception as e:
|
66
|
-
raise ValueError(f"failed to validate field {k}") from e
|
67
|
-
|
68
|
-
def _process_output(self, out_: dict = None):
|
69
|
-
for k, v in out_.items():
|
70
|
-
try:
|
71
|
-
valid_ = self._validate_field(
|
72
|
-
k,
|
73
|
-
v,
|
74
|
-
choices=self._get_field_attr(k, "choices", None),
|
75
|
-
keys=self._get_field_attr(k, "keys", None),
|
76
|
-
fix_=self.fix_output,
|
77
|
-
**self._get_field_attr(k, "validation_kwargs", {}),
|
78
|
-
)
|
79
|
-
if not valid_:
|
80
|
-
raise ValueError(f"failed to validate field {k}")
|
81
|
-
except Exception as e:
|
82
|
-
raise ValueError(f"failed to validate field {k}") from e
|
83
|
-
|
84
|
-
if self.is_completed:
|
85
|
-
self.filled = True
|
86
|
-
|
87
|
-
def _validate_field(self, k, v, choices=None, keys=None, fix_=False, **kwargs):
|
88
|
-
annotation = self.field_annotations[k]
|
89
|
-
|
90
|
-
if choices:
|
91
|
-
if choices and not isinstance(choices, list):
|
92
|
-
try:
|
93
|
-
choices = [i.value for i in choices]
|
94
|
-
except Exception as e:
|
95
|
-
raise ValueError(f"failed to get choices for field {k}") from e
|
96
|
-
v_ = validation_funcs["enum"](v, choices=choices, fix_=fix_, **kwargs)
|
97
|
-
if v_ not in choices:
|
98
|
-
raise ValueError(f"{v} is not in chocies {choices}")
|
99
|
-
setattr(self, k, v_)
|
100
|
-
return True
|
101
|
-
|
102
|
-
if any("actionrequest" in i for i in annotation):
|
103
|
-
self.__setattr__(k, validation_funcs["action"](v))
|
104
|
-
return True
|
105
|
-
|
106
|
-
if "bool" in annotation and "str" not in annotation:
|
107
|
-
self.__setattr__(k, validation_funcs["bool"](v, fix_=fix_, **kwargs))
|
108
|
-
return True
|
109
|
-
|
110
|
-
if (
|
111
|
-
any([i in annotation for i in ["int", "float", "number"]])
|
112
|
-
and "str" not in annotation
|
113
|
-
):
|
114
|
-
if "float" in annotation:
|
115
|
-
kwargs["num_type"] = float
|
116
|
-
if "precision" not in kwargs:
|
117
|
-
kwargs["precision"] = 10
|
118
|
-
|
119
|
-
self.__setattr__(k, validation_funcs["number"](v, fix_=fix_, **kwargs))
|
120
|
-
return True
|
121
|
-
|
122
|
-
if "dict" in annotation:
|
123
|
-
if "str" not in annotation or keys:
|
124
|
-
v_ = validation_funcs["dict"](v, keys=keys, fix_=fix_, **kwargs)
|
125
|
-
setattr(self, k, v_)
|
126
|
-
return True
|
127
|
-
|
128
|
-
if "str" in annotation:
|
129
|
-
self.__setattr__(k, validation_funcs["str"](v, fix_=fix_, **kwargs))
|
130
|
-
return True
|
131
|
-
|
132
|
-
return False
|
133
|
-
|
134
|
-
|
135
|
-
# from enum import Enum
|
136
|
-
# from pydantic import Field
|
137
|
-
|
138
|
-
# class EXAMPLES(str, Enum):
|
139
|
-
# EXAMPLE1 = "example1"
|
140
|
-
# EXAMPLE2 = "example2"
|
141
|
-
# EXAMPLE3 = "example3"
|
142
|
-
|
143
|
-
# class Form1(Form):
|
144
|
-
# a: str | EXAMPLES = Field("example3", choices=EXAMPLES)
|
145
|
-
# b: str = "input2"
|
146
|
-
# c: str|None = Field(None, choices=["output1", "output2"])
|
147
|
-
# d: float | None = Field(None, json_schema_extra={"validation_kwargs":{"num_type": float, "precision": 2}})
|
148
|
-
# assignment: str='a, b -> c, d'
|
149
|
-
# form_name: str='custom_form'
|
150
|
-
# description: str='Test Form'
|
151
|
-
|
152
|
-
# form = Form1()
|
153
|
-
# form.process(out_ = {"c": "output1", "d": "1.1"})
|
154
|
-
|
155
|
-
|
156
|
-
"""
|
157
|
-
usage pattern:
|
158
|
-
|
159
|
-
from pydantic import Field
|
160
|
-
|
161
|
-
class JokeForm(Form):
|
162
|
-
|
163
|
-
# 1. use the key kwarg to specify the key in the dict,
|
164
|
-
# which will be used validate the field value
|
165
|
-
|
166
|
-
# 2. use fix flag to indicate whether to fix the value
|
167
|
-
# if it is not valid, default to True
|
168
|
-
|
169
|
-
material: dict = Field(
|
170
|
-
...,
|
171
|
-
description="materials to read",
|
172
|
-
keys=["title", "author", "year"], # use fix flag for dict key can be dangerous
|
173
|
-
fix=True # the validator will force the value to match
|
174
|
-
) # the provided keys, which might pollute the data
|
175
|
-
|
176
|
-
|
177
|
-
# 3. use choices to specify the available options
|
178
|
-
topic: str = Field(
|
179
|
-
...,
|
180
|
-
description="topic to write joke on",
|
181
|
-
choices=["animal", "food", "people"],
|
182
|
-
)
|
183
|
-
|
184
|
-
# you can also use enum or subclass of Enum to explicitly
|
185
|
-
# declare that the field requires
|
186
|
-
# the value to be one of the provided choices
|
187
|
-
|
188
|
-
from enum import Enum
|
189
|
-
|
190
|
-
class TOPIC(str, Enum):
|
191
|
-
ANIMAL = "animal"
|
192
|
-
FOOD = "food"
|
193
|
-
PEOPLE = "people"
|
194
|
-
|
195
|
-
|
196
|
-
topic: TOPIC = Field(
|
197
|
-
default = TOPIC.ANIMAL,
|
198
|
-
description="topic to write joke on",
|
199
|
-
choices=TOPIC,
|
200
|
-
fix_input=True, # fix_input and fix_output are used to indicate
|
201
|
-
) # whether to fix invalid values, default to True
|
202
|
-
|
203
|
-
# 4. using optional fields
|
204
|
-
# you can add None in type annotation to indicate that the field is optional
|
205
|
-
# otherwise there will be a validation error if there is no default
|
206
|
-
# field value, and value is also not provided at initialization
|
207
|
-
|
208
|
-
joke: str | None = Field(
|
209
|
-
default = None,
|
210
|
-
description = "joke to write"
|
211
|
-
)
|
212
|
-
|
213
|
-
# 5. using validation_kwargs
|
214
|
-
# you can use validation_kwargs to specify the validation parameters
|
215
|
-
# there are built-in validators for numbers
|
216
|
-
|
217
|
-
rating: float | None = Field(
|
218
|
-
default=None,
|
219
|
-
description="rating, a numerical value",
|
220
|
-
validation_kwargs={
|
221
|
-
"upper_bound": 10, # this will ensure the value for this field
|
222
|
-
"lower_bound": 0, # is a number between upper_bound and lower_bound
|
223
|
-
"num_type": "float",
|
224
|
-
"precision": 2}
|
225
|
-
)
|
226
|
-
|
227
|
-
|
228
|
-
# 6. using assignment
|
229
|
-
# in a form, you do not need to specify the input output for each field,
|
230
|
-
# instead, you can use assignment to specify the input and output of the form
|
231
|
-
|
232
|
-
# an assignment is a string that describes the input and output of the form
|
233
|
-
# it is used to generate a composable set of fields specific to different context
|
234
|
-
# from the same custom Form class you specified, for example, two fields "a" and "b",
|
235
|
-
# in one form, you can specify the assignment as "a -> b", and in another form,
|
236
|
-
# you can specify the assignment as "b -> a", and the system will generate two different
|
237
|
-
# instruction for the worker to perform.
|
238
|
-
|
239
|
-
assignment: str = Field(
|
240
|
-
default=...,
|
241
|
-
examples=["input1, input2 -> output"]
|
242
|
-
)
|
243
|
-
|
244
|
-
# for example, "work -> review, rating" and "review, work -> rating" have completely different
|
245
|
-
# meanings, and the system will generate two different forms for the worker to complete
|
246
|
-
# the former means, a review task of a work performance with corresponding rating for the work
|
247
|
-
# the latter means, a rating task on the review quality for the work performance,
|
248
|
-
# such as, whether it is fair for the rating
|
249
|
-
|
250
|
-
|
251
|
-
# 7. general guidance
|
252
|
-
# in principle, a field should only be filled once, thus making form a single use object
|
253
|
-
# the form has a filled flag to indicate whether the form is completed
|
254
|
-
|
255
|
-
"""
|
256
|
-
|
257
|
-
# import unittest
|
258
|
-
# from pydantic import Field
|
259
|
-
|
260
|
-
|
261
|
-
# class Form1(Form):
|
262
|
-
# input1: str = Field("input1", json_schema_extra={"choices": ["option1", "option2"]})
|
263
|
-
# input2: str = "input2"
|
264
|
-
# output1: str = "output1"
|
265
|
-
|
266
|
-
|
267
|
-
# class TestForm(unittest.TestCase):
|
268
|
-
|
269
|
-
# def test_default_initialization(self):
|
270
|
-
# class Form1(Form):
|
271
|
-
# input1: str = "input1"
|
272
|
-
# input2: str = "input2"
|
273
|
-
# output1: str = "output1"
|
274
|
-
# assignment: str = "input1, input2 -> output1"
|
275
|
-
|
276
|
-
# form = Form1()
|
277
|
-
# self.assertEqual(form.form_name, "default_form")
|
278
|
-
# self.assertIsNone(form.description)
|
279
|
-
# self.assertEqual(form.input_fields, ["input1", "input2"])
|
280
|
-
# self.assertEqual(form.output_fields, ["output1"])
|
281
|
-
# self.assertFalse(form.filled)
|
282
|
-
# self.assertFalse(form.fix_input)
|
283
|
-
# self.assertTrue(form.fix_output)
|
284
|
-
|
285
|
-
# def test_custom_initialization(self):
|
286
|
-
# class Form1(Form):
|
287
|
-
# a: str = "input1"
|
288
|
-
# b: str = "input2"
|
289
|
-
# c: str = "output1"
|
290
|
-
# assignment: str = "a, b -> c"
|
291
|
-
# form_name: str = "custom_form"
|
292
|
-
# description: str = "Test Form"
|
293
|
-
|
294
|
-
# form = Form1()
|
295
|
-
# self.assertEqual(form.form_name, "custom_form")
|
296
|
-
# self.assertEqual(form.description, "Test Form")
|
297
|
-
# self.assertEqual(form.input_fields, ["a", "b"])
|
298
|
-
# self.assertEqual(form.output_fields, ["c"])
|
299
|
-
|
300
|
-
# def test_process_inputs_outputs(self):
|
301
|
-
# class Form1(Form):
|
302
|
-
# input1: str = Field(
|
303
|
-
# "option1", json_schema_extra={"choices": ["option1", "option2"]}
|
304
|
-
# )
|
305
|
-
# input2: str = "input2"
|
306
|
-
# output1: str = "output1"
|
307
|
-
# assignment: str = "input1, input2 -> output1"
|
308
|
-
|
309
|
-
# form = Form1()
|
310
|
-
# setattr(form, "input1", "option1")
|
311
|
-
# # Test processing valid input
|
312
|
-
# form.process(in_=True) # Should process without error
|
313
|
-
|
314
|
-
# setattr(form, "input1", "option3")
|
315
|
-
# # Test processing invalid input
|
316
|
-
# with self.assertRaises(ValueError):
|
317
|
-
# form.process(in_=True) # Should raise error
|
318
|
-
|
319
|
-
# def test_check_complete(self):
|
320
|
-
# class Form1(Form):
|
321
|
-
# input1: str = "input1"
|
322
|
-
# input2: str = "input2"
|
323
|
-
# output1: str = "output1"
|
324
|
-
|
325
|
-
# form = Form1(assignment="input1, input2 -> output1")
|
326
|
-
# setattr(form, "input1", "value1")
|
327
|
-
# setattr(form, "input2", "value2")
|
328
|
-
# setattr(form, "output1", "result1")
|
329
|
-
# self.assertTrue(form.is_completed)
|
330
|
-
|
331
|
-
# def test_input_output_fields_parsing(self):
|
332
|
-
# class Form1(Form):
|
333
|
-
# x: str = "input1"
|
334
|
-
# y: str = "input2"
|
335
|
-
# z: str = "output1"
|
336
|
-
|
337
|
-
# form = Form1(assignment="x, y -> z")
|
338
|
-
# self.assertEqual(form.input_fields, ["x", "y"])
|
339
|
-
# self.assertEqual(form.output_fields, ["z"])
|
340
|
-
|
341
|
-
# def test_validation_failure(self):
|
342
|
-
# class Form1(Form):
|
343
|
-
# input1: str = Field(
|
344
|
-
# None, json_schema_extra={"choices": ["option1", "option2"]}
|
345
|
-
# )
|
346
|
-
# input2: str = "input2"
|
347
|
-
# output1: str = "output1"
|
348
|
-
# assignment: str = "input1, input2 -> output1"
|
349
|
-
|
350
|
-
# # Test handling invalid input choice
|
351
|
-
# with self.assertRaises(ValueError):
|
352
|
-
# form = Form1()
|
353
|
-
|
354
|
-
# def test_output_assignment(self):
|
355
|
-
# class Form1(Form):
|
356
|
-
# input1: str = "value1"
|
357
|
-
# output1: str = Field("output1", choices=["result1", "result2"])
|
358
|
-
# assignment: str = "input1 -> output1"
|
359
|
-
|
360
|
-
# form = Form1()
|
361
|
-
# # Test handling invalid output choice
|
362
|
-
# try:
|
363
|
-
# form.process(out_={"output1": "result3"})
|
364
|
-
# except ValueError:
|
365
|
-
# pass
|
366
|
-
|
367
|
-
# form.process(out_={"output1": "result3"}) # Should process without error
|
368
|
-
|
369
|
-
|
370
|
-
# if __name__ == "__main__":
|
371
|
-
# unittest.main()
|
@@ -1,289 +0,0 @@
|
|
1
|
-
from typing import Any
|
2
|
-
from pydantic import Field
|
3
|
-
from lionagi.experimental.report.form import Form
|
4
|
-
from lionagi.core.generic import BaseComponent
|
5
|
-
from lionagi.experimental.report.util import get_input_output_fields
|
6
|
-
|
7
|
-
"""
|
8
|
-
## Report Usage Pattern
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
requirements,
|
16
|
-
the assignments name should be consistent within a report,
|
17
|
-
meaning, all forms will refer to the same field value when using the same name.
|
18
|
-
|
19
|
-
the same input field can be used in multiple forms, but each unique output field
|
20
|
-
should be filled only once.
|
21
|
-
meaning, not two forms should have the any output fields in common.
|
22
|
-
|
23
|
-
a filled field cannot be None
|
24
|
-
|
25
|
-
usage pattern:
|
26
|
-
|
27
|
-
class Report1(Report):
|
28
|
-
a: Any = None
|
29
|
-
b: Any = None
|
30
|
-
c: Any = None
|
31
|
-
d: Any = None
|
32
|
-
e: Any = None
|
33
|
-
f: Any = None
|
34
|
-
|
35
|
-
assignments: list = [
|
36
|
-
"a, b -> c",
|
37
|
-
"c -> d",
|
38
|
-
"b, d -> e"
|
39
|
-
]
|
40
|
-
|
41
|
-
this means that the report will have 3 work steps, each corresponding to an unique
|
42
|
-
form. the context for the report is the fields that cannot be output by any work
|
43
|
-
teps in the report.
|
44
|
-
|
45
|
-
the deliverable for the report is the fields that are output produced by the work
|
46
|
-
steps but not used as input for any other work steps in the same report.
|
47
|
-
|
48
|
-
under the hood:
|
49
|
-
|
50
|
-
the report first create three forms and add to its forms dictionary
|
51
|
-
|
52
|
-
form1 = Form1(assignment="a, b -> c")
|
53
|
-
form2 = Form2(assignment="c -> d")
|
54
|
-
form3 = Form3(assignment="b -> e")
|
55
|
-
form3 = Form3(assignment="d, e -> f")
|
56
|
-
|
57
|
-
The Form1, Form2, Form3 can be a single form class or multiple form classes,
|
58
|
-
as long as they have the same fields to interact
|
59
|
-
|
60
|
-
report is created by intaking different context inputs, in this case, we
|
61
|
-
need to provide a and b to the report
|
62
|
-
|
63
|
-
report let work scheduler know which form to fill next, by using the next_forms() method,
|
64
|
-
this method check if the dependencies of a form are filled, if so, all such forms are
|
65
|
-
deemed to be next to fill.
|
66
|
-
|
67
|
-
so to begin with we need the context fields, a and b, then the report checks the
|
68
|
-
next forms, notice, for (a, b -> c), we have a and b, so this form can be filled next
|
69
|
-
also for (b -> e), we have b, so this form can also be filled next
|
70
|
-
|
71
|
-
thus the report will first send forms of those two assignments to the scheduler.
|
72
|
-
|
73
|
-
as work gets done, forms get filled, the completed forms will be sent back to scheduler,
|
74
|
-
and the scheduler is in charge of filling in the fields onto the report. After each
|
75
|
-
time a report gets new fields filled, the scheduler will check the next forms to fill,
|
76
|
-
and the process continues until all forms are filled.
|
77
|
-
|
78
|
-
so as (a, b -> c) is done, we get c, thus c -> d is then the next form
|
79
|
-
and d, e -> f is the last form to fill
|
80
|
-
once all forms are filled and all fields transfered to reports, the report is completed.
|
81
|
-
|
82
|
-
"""
|
83
|
-
|
84
|
-
|
85
|
-
class Report(BaseComponent):
|
86
|
-
|
87
|
-
report_name: str = Field(
|
88
|
-
default="default_report",
|
89
|
-
)
|
90
|
-
description: Any = Field(default=None)
|
91
|
-
task: Any = Field(default=None)
|
92
|
-
forms: dict[str, Form] = Field(
|
93
|
-
default_factory=dict,
|
94
|
-
description="A dictionary of forms related to the report, in {form.id_: form} format.",
|
95
|
-
)
|
96
|
-
context: dict = Field(default_factory=dict, description="context for the report")
|
97
|
-
deliverable: dict = Field(
|
98
|
-
default_factory=dict, description="deliverable for the report"
|
99
|
-
)
|
100
|
-
intermediate: dict = Field(
|
101
|
-
default_factory=dict, description="intermediate fields for the report"
|
102
|
-
)
|
103
|
-
filled: bool = Field(False, description="whether the report is completed")
|
104
|
-
assignments: list = Field([], description="assignment for the report")
|
105
|
-
|
106
|
-
def fill_report(self, form: Form | str):
|
107
|
-
form = self.forms[form] if isinstance(form, str) else form
|
108
|
-
if not form.filled or form not in self.forms.values():
|
109
|
-
raise ValueError("The form is not filled or not in the report.")
|
110
|
-
|
111
|
-
for i in form.input_fields:
|
112
|
-
if i not in self._filled_fields:
|
113
|
-
setattr(self, i, getattr(form, i))
|
114
|
-
if i not in self.deliverable:
|
115
|
-
self.intermediate[i] = getattr(form, i)
|
116
|
-
|
117
|
-
for i in form.output_fields:
|
118
|
-
setattr(self, i, getattr(form, i))
|
119
|
-
if i not in self.deliverable:
|
120
|
-
self.intermediate[i] = getattr(form, i)
|
121
|
-
|
122
|
-
@property
|
123
|
-
def work_fields(self):
|
124
|
-
"""
|
125
|
-
all work fields across all forms, including intermediate output fields
|
126
|
-
"""
|
127
|
-
all_fields = []
|
128
|
-
for form in self.forms.values():
|
129
|
-
all_fields.extend(form.work_fields)
|
130
|
-
return list(set(all_fields))
|
131
|
-
|
132
|
-
@property
|
133
|
-
def is_completed(self):
|
134
|
-
return all([hasattr(self, i) for i in self.work_fields])
|
135
|
-
|
136
|
-
@property
|
137
|
-
def is_workable(self):
|
138
|
-
context_fields, deliverable_fields = get_input_output_fields(self.assignment)
|
139
|
-
context_fields.extend(deliverable_fields)
|
140
|
-
|
141
|
-
# check whether all work fields are assigned in the forms for the report
|
142
|
-
if not all([i in self.work_fields for i in context_fields]):
|
143
|
-
raise ValueError(
|
144
|
-
f"Not all work fields are assigned in the forms for the report."
|
145
|
-
)
|
146
|
-
|
147
|
-
outs = []
|
148
|
-
for form in self.forms.values():
|
149
|
-
outs.extend(form.output_fields)
|
150
|
-
|
151
|
-
if len(outs) != len(set(outs)):
|
152
|
-
raise ValueError(f"Output fields should be unique across all forms.")
|
153
|
-
|
154
|
-
inputs = []
|
155
|
-
for form in self.forms.values():
|
156
|
-
inputs.extend(form.input_fields)
|
157
|
-
|
158
|
-
inputs = [i for i in inputs if i not in outs]
|
159
|
-
if not all([i in self.work_fields for i in inputs]):
|
160
|
-
raise ValueError(
|
161
|
-
f"Not all input fields are assigned in the forms for the report."
|
162
|
-
)
|
163
|
-
|
164
|
-
return True
|
165
|
-
|
166
|
-
def next_forms(self):
|
167
|
-
to_do = []
|
168
|
-
for i in self._unfilled_forms:
|
169
|
-
if all([j in self._filled_fields for j in i.input_fields]):
|
170
|
-
to_do.append(i)
|
171
|
-
return to_do[0] if len(to_do) == 1 else to_do or None
|
172
|
-
|
173
|
-
@property
|
174
|
-
def _filled_forms(self):
|
175
|
-
return [form for form in self.forms.values() if form.filled]
|
176
|
-
|
177
|
-
@property
|
178
|
-
def _unfilled_forms(self):
|
179
|
-
return [form for form in self.forms.values() if not form.filled]
|
180
|
-
|
181
|
-
@property
|
182
|
-
def _filled_fields(self):
|
183
|
-
filled_fields = []
|
184
|
-
for i in self.work_fields:
|
185
|
-
if getattr(self, i, None) is not None:
|
186
|
-
filled_fields.append(i)
|
187
|
-
return filled_fields
|
188
|
-
|
189
|
-
@property
|
190
|
-
def _unfilled_fields(self):
|
191
|
-
return [i for i in self.work_fields if i not in self._filled_fields]
|
192
|
-
|
193
|
-
|
194
|
-
# import unittest
|
195
|
-
# from enum import Enum
|
196
|
-
# from typing import Any
|
197
|
-
# from pydantic import Field
|
198
|
-
# from lionagi import logging as _logging
|
199
|
-
# from lionagi.experimental.form.form import Form
|
200
|
-
# from lionagi.core.generic import BaseComponent
|
201
|
-
# from lionagi.experimental.form.util import get_input_output_fields
|
202
|
-
|
203
|
-
|
204
|
-
# class EXAMPLES(str, Enum):
|
205
|
-
# EXAMPLE1 = "example1"
|
206
|
-
# EXAMPLE2 = "example2"
|
207
|
-
# EXAMPLE3 = "example3"
|
208
|
-
|
209
|
-
|
210
|
-
# class Form1(Form):
|
211
|
-
# a: str | EXAMPLES = Field(EXAMPLES.EXAMPLE3, choices=list(EXAMPLES))
|
212
|
-
# b: str = "input2"
|
213
|
-
# c: str | None = Field(None, choices=["output1", "output2"])
|
214
|
-
# d: float | None = Field(None)
|
215
|
-
# assignment: str = "a, b -> c, d"
|
216
|
-
# form_name: str = "custom_form"
|
217
|
-
# description: str = "Test Form"
|
218
|
-
|
219
|
-
|
220
|
-
# class TestReport(unittest.TestCase):
|
221
|
-
# def setUp(self):
|
222
|
-
# self.report = Report(assignment="a, b -> c, d")
|
223
|
-
# self.form1 = Form1(assignment="a, b -> c, d")
|
224
|
-
# self.report.forms[self.form1.id_] = self.form1
|
225
|
-
|
226
|
-
# def test_initialization(self):
|
227
|
-
# self.assertEqual(self.report.report_name, "default_report")
|
228
|
-
# self.assertIn(self.form1.id_, self.report.forms)
|
229
|
-
|
230
|
-
# def test_fill_report(self):
|
231
|
-
# self.form1.process(out_={"c": "output1", "d": 1.1})
|
232
|
-
# self.report.fill_report(self.form1)
|
233
|
-
# self.assertTrue(self.form1.filled)
|
234
|
-
# self.assertEqual(self.report.intermediate["c"], "output1")
|
235
|
-
|
236
|
-
# def test_report_completeness(self):
|
237
|
-
# self.form1.process(out_={"c": "output1", "d": 1.1})
|
238
|
-
# self.report.fill_report(self.form1)
|
239
|
-
# self.assertTrue(self.report.is_completed)
|
240
|
-
|
241
|
-
# def test_report_workability(self):
|
242
|
-
# # self.form1.process(out_={'c': 'output1', 'd': 1.1})
|
243
|
-
# # self.report.fill_report(self.form1)
|
244
|
-
# self.assertTrue(self.report.is_workable)
|
245
|
-
|
246
|
-
# def test_handling_invalid_form_id(self):
|
247
|
-
# with self.assertRaises(KeyError):
|
248
|
-
# self.report.fill_report("nonexistent_form")
|
249
|
-
|
250
|
-
# def test_next_forms_logic(self):
|
251
|
-
# next_forms = self.report.next_forms()
|
252
|
-
# self.assertEqual(next_forms, None)
|
253
|
-
|
254
|
-
# def test_work_fields_property(self):
|
255
|
-
# self.form1.process(out_={"c": "output1", "d": 1.1})
|
256
|
-
# self.report.fill_report(self.form1)
|
257
|
-
# self.assertIn("a", self.report.work_fields)
|
258
|
-
# self.assertIn("b", self.report.work_fields)
|
259
|
-
# self.assertIn("c", self.report.work_fields)
|
260
|
-
# self.assertIn("d", self.report.work_fields)
|
261
|
-
|
262
|
-
# def test_filled_forms_property(self):
|
263
|
-
# self.form1.process(out_={"c": "output1", "d": 1.1})
|
264
|
-
# self.report.fill_report(self.form1)
|
265
|
-
# self.assertIn(self.form1, self.report._filled_forms)
|
266
|
-
|
267
|
-
# def test_unfilled_forms_property(self):
|
268
|
-
# self.assertIn(self.form1, self.report._unfilled_forms)
|
269
|
-
# self.form1.process(out_={"c": "output1", "d": 1.1})
|
270
|
-
# self.report.fill_report(self.form1)
|
271
|
-
# self.assertNotIn(self.form1, self.report._unfilled_forms)
|
272
|
-
|
273
|
-
# def test_filled_fields_property(self):
|
274
|
-
# self.form1.process(out_={"c": "output1", "d": 1.1})
|
275
|
-
# self.report.fill_report(self.form1)
|
276
|
-
# self.assertIn("c", self.report._filled_fields)
|
277
|
-
# self.assertIn("d", self.report._filled_fields)
|
278
|
-
|
279
|
-
# def test_unfilled_fields_property(self):
|
280
|
-
# self.assertIn("c", self.report._unfilled_fields)
|
281
|
-
# self.assertIn("d", self.report._unfilled_fields)
|
282
|
-
# self.form1.process(out_={"c": "output1", "d": 1.1})
|
283
|
-
# self.report.fill_report(self.form1)
|
284
|
-
# self.assertNotIn("c", self.report._unfilled_fields)
|
285
|
-
# self.assertNotIn("d", self.report._unfilled_fields)
|
286
|
-
|
287
|
-
|
288
|
-
# if __name__ == "__main__":
|
289
|
-
# unittest.main()
|
@@ -1,30 +0,0 @@
|
|
1
|
-
from enum import Enum
|
2
|
-
from typing import Any, Dict, List
|
3
|
-
from pydantic import Field
|
4
|
-
from lionagi.core.generic import BaseComponent
|
5
|
-
|
6
|
-
|
7
|
-
class WorkStatus(str, Enum):
|
8
|
-
"""Enum to represent different statuses of work."""
|
9
|
-
|
10
|
-
PENDING = "PENDING"
|
11
|
-
IN_PROGRESS = "IN_PROGRESS"
|
12
|
-
COMPLETED = "COMPLETED"
|
13
|
-
FAILED = "FAILED"
|
14
|
-
CANCELLED = "CANCELLED"
|
15
|
-
|
16
|
-
|
17
|
-
class Work(BaseComponent):
|
18
|
-
"""Base component for handling individual units of work."""
|
19
|
-
|
20
|
-
form_id: str = Field(..., description="ID of the form for this work")
|
21
|
-
priority: int = Field(default=0, description="Priority of the work")
|
22
|
-
status: WorkStatus = Field(
|
23
|
-
default=WorkStatus.PENDING, description="Current status of the work"
|
24
|
-
)
|
25
|
-
deliverables: Dict[str, Any] | list = Field(
|
26
|
-
default={}, description="Deliverables produced by the work"
|
27
|
-
)
|
28
|
-
dependencies: List["Work"] = Field(
|
29
|
-
default_factory=list, description="List of work items this work depends on"
|
30
|
-
)
|