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,67 @@
|
|
1
|
+
import asyncio
|
2
|
+
import unittest
|
3
|
+
from unittest.mock import patch
|
4
|
+
from lionagi.libs.ln_queue import AsyncQueue
|
5
|
+
|
6
|
+
|
7
|
+
class TestAsyncQueue(unittest.TestCase):
|
8
|
+
def setUp(self):
|
9
|
+
self.loop = asyncio.new_event_loop()
|
10
|
+
asyncio.set_event_loop(self.loop)
|
11
|
+
self.queue = AsyncQueue(max_concurrent_tasks=3)
|
12
|
+
|
13
|
+
def tearDown(self):
|
14
|
+
self.loop.close()
|
15
|
+
|
16
|
+
def test_enqueue_dequeue(self):
|
17
|
+
async def test():
|
18
|
+
await self.queue.enqueue("task1")
|
19
|
+
result = await self.queue.dequeue()
|
20
|
+
self.assertEqual(result, "task1")
|
21
|
+
|
22
|
+
self.loop.run_until_complete(test())
|
23
|
+
|
24
|
+
def test_stop(self):
|
25
|
+
async def test():
|
26
|
+
await self.queue.stop()
|
27
|
+
self.assertTrue(self.queue.stopped())
|
28
|
+
|
29
|
+
self.loop.run_until_complete(test())
|
30
|
+
|
31
|
+
@patch("lionagi.libs.ln_func_call.rcall", autospec=True)
|
32
|
+
def test_process_requests(self, mock_rcall):
|
33
|
+
future = asyncio.Future()
|
34
|
+
future.set_result("Processed successfully")
|
35
|
+
mock_rcall.return_value = future
|
36
|
+
|
37
|
+
async def processor(task):
|
38
|
+
return f"Processed {task}"
|
39
|
+
|
40
|
+
async def add_tasks():
|
41
|
+
for i in range(5):
|
42
|
+
await self.queue.enqueue(f"task{i}")
|
43
|
+
# This ensures that the queue stops after all tasks are added
|
44
|
+
await self.queue.stop()
|
45
|
+
|
46
|
+
async def process():
|
47
|
+
await self.queue.process_requests(processor, retry_kwargs={"retries": 2})
|
48
|
+
|
49
|
+
self.loop.create_task(add_tasks())
|
50
|
+
self.loop.run_until_complete(process())
|
51
|
+
# mock_rcall.assert_called()
|
52
|
+
|
53
|
+
def test_concurrency_limit(self):
|
54
|
+
async def add_tasks():
|
55
|
+
for i in range(10):
|
56
|
+
await self.queue.enqueue(f"task{i}")
|
57
|
+
|
58
|
+
async def process():
|
59
|
+
await asyncio.sleep(0.1)
|
60
|
+
await self.queue.stop()
|
61
|
+
|
62
|
+
self.loop.run_until_complete(add_tasks())
|
63
|
+
self.loop.run_until_complete(process())
|
64
|
+
|
65
|
+
|
66
|
+
if __name__ == "__main__":
|
67
|
+
unittest.main()
|
File without changes
|
@@ -0,0 +1,206 @@
|
|
1
|
+
import unittest
|
2
|
+
from lionagi.core.collections.abc.component import (
|
3
|
+
Component,
|
4
|
+
LionValueError,
|
5
|
+
LionTypeError,
|
6
|
+
)
|
7
|
+
import pandas as pd
|
8
|
+
from datetime import datetime
|
9
|
+
import json
|
10
|
+
|
11
|
+
|
12
|
+
class TestComponent(unittest.TestCase):
|
13
|
+
|
14
|
+
def setUp(self):
|
15
|
+
"""Set up a basic Component instance for testing."""
|
16
|
+
self.component = Component()
|
17
|
+
|
18
|
+
def test_initialization(self):
|
19
|
+
"""Test basic initialization and attributes."""
|
20
|
+
self.assertIsInstance(self.component.ln_id, str)
|
21
|
+
self.assertIsInstance(self.component.timestamp, str)
|
22
|
+
self.assertIsInstance(self.component.metadata, dict)
|
23
|
+
self.assertIsNone(self.component.content)
|
24
|
+
self.assertEqual(self.component.embedding, [])
|
25
|
+
|
26
|
+
def test_setting_attributes(self):
|
27
|
+
"""Test setting and updating attributes."""
|
28
|
+
self.component.content = 1
|
29
|
+
self.assertEqual(self.component.content, 1)
|
30
|
+
self.assertIn("content", self.component.metadata["last_updated"])
|
31
|
+
|
32
|
+
def test_class_name(self):
|
33
|
+
"""Test the class_name property."""
|
34
|
+
self.assertEqual(self.component.class_name, "Component")
|
35
|
+
|
36
|
+
def test_to_dict(self):
|
37
|
+
"""Test converting to dictionary."""
|
38
|
+
self.component.content = "example content"
|
39
|
+
dict_repr = self.component.to_dict()
|
40
|
+
self.assertEqual(dict_repr["content"], "example content")
|
41
|
+
self.assertEqual(dict_repr["lion_class"], "Component")
|
42
|
+
|
43
|
+
def test_to_json_str(self):
|
44
|
+
"""Test converting to JSON string."""
|
45
|
+
self.component.content = "example content"
|
46
|
+
json_str = self.component.to_json_str()
|
47
|
+
self.assertIn('"content": "example content"', json_str)
|
48
|
+
|
49
|
+
def test_to_xml(self):
|
50
|
+
"""Test converting to XML string."""
|
51
|
+
self.component.content = "example content"
|
52
|
+
xml_str = self.component.to_xml()
|
53
|
+
self.assertIn("<content>example content</content>", xml_str)
|
54
|
+
|
55
|
+
def test_to_pd_series(self):
|
56
|
+
"""Test converting to Pandas Series."""
|
57
|
+
self.component.content = "example content"
|
58
|
+
series = self.component.to_pd_series()
|
59
|
+
self.assertEqual(series["content"], "example content")
|
60
|
+
|
61
|
+
def test_from_obj_dict(self):
|
62
|
+
"""Test creating a Component from a dictionary."""
|
63
|
+
dict_obj = {"a": 1, "b": 2}
|
64
|
+
new_component = Component.from_obj(dict_obj)
|
65
|
+
self.assertEqual(new_component.metadata["a"], 1)
|
66
|
+
self.assertEqual(new_component.metadata["b"], 2)
|
67
|
+
|
68
|
+
def test_from_obj_str(self):
|
69
|
+
"""Test creating a Component from a JSON string."""
|
70
|
+
json_str = '{"a": 1, "b": 2}'
|
71
|
+
new_component = Component.from_obj(json_str)
|
72
|
+
self.assertEqual(new_component.metadata["a"], 1)
|
73
|
+
self.assertEqual(new_component.metadata["b"], 2)
|
74
|
+
|
75
|
+
def test_from_obj_fuzzy_str(self):
|
76
|
+
"""Test creating a Component from a fuzzy JSON string."""
|
77
|
+
fuzzy_json_str = '{"name": "John", "age": 30, "city": ["New York", "DC", "LA"]'
|
78
|
+
new_component = Component.from_obj(fuzzy_json_str, fuzzy_parse=True)
|
79
|
+
self.assertEqual(new_component.metadata["name"], "John")
|
80
|
+
self.assertEqual(new_component.metadata["age"], 30)
|
81
|
+
self.assertEqual(new_component.metadata["city"], ["New York", "DC", "LA"])
|
82
|
+
|
83
|
+
def test_from_obj_series(self):
|
84
|
+
"""Test creating a Component from a Pandas Series."""
|
85
|
+
series_obj = pd.Series({"a": 1, "b": 2})
|
86
|
+
new_component = Component.from_obj(series_obj)
|
87
|
+
self.assertEqual(new_component.metadata["a"], 1)
|
88
|
+
self.assertEqual(new_component.metadata["b"], 2)
|
89
|
+
|
90
|
+
def test_from_obj_dataframe(self):
|
91
|
+
"""Test creating Components from a Pandas DataFrame."""
|
92
|
+
df = pd.DataFrame({"a": [1, 2], "b": [3, 4]}, index=["row1", "row2"])
|
93
|
+
components = Component.from_obj(df)
|
94
|
+
self.assertEqual(len(components), 2)
|
95
|
+
self.assertEqual(components[0].metadata["a"], 1)
|
96
|
+
self.assertEqual(components[0].metadata["b"], 3)
|
97
|
+
self.assertEqual(components[1].metadata["a"], 2)
|
98
|
+
self.assertEqual(components[1].metadata["b"], 4)
|
99
|
+
|
100
|
+
def test_metadata_manipulation(self):
|
101
|
+
"""Test manipulation of metadata."""
|
102
|
+
self.component._meta_insert(["new_key"], "new_value")
|
103
|
+
self.assertEqual(self.component.metadata["new_key"], "new_value")
|
104
|
+
self.component._meta_set(["new_key"], "updated_value")
|
105
|
+
self.assertEqual(self.component.metadata["new_key"], "updated_value")
|
106
|
+
nested_value = {"a": 1, "b": 2}
|
107
|
+
self.component._meta_insert(["nested", 0], nested_value)
|
108
|
+
self.assertEqual(self.component.metadata["nested"][0], nested_value)
|
109
|
+
self.assertEqual(self.component._meta_get(["nested", 0, "a"]), 1)
|
110
|
+
|
111
|
+
def test_invalid_metadata_assignment(self):
|
112
|
+
"""Test invalid direct assignment to metadata."""
|
113
|
+
with self.assertRaises(AttributeError):
|
114
|
+
self.component.metadata = {}
|
115
|
+
|
116
|
+
def test_field_annotations(self):
|
117
|
+
"""Test field annotations."""
|
118
|
+
annotations = self.component._field_annotations
|
119
|
+
self.assertIn("ln_id", annotations)
|
120
|
+
self.assertIn("timestamp", annotations)
|
121
|
+
|
122
|
+
def test_dynamic_field_addition(self):
|
123
|
+
"""Test adding fields dynamically to the Component."""
|
124
|
+
self.component._add_field(
|
125
|
+
"welcome", str, default="new value", value="hello world again"
|
126
|
+
)
|
127
|
+
self.assertEqual(
|
128
|
+
self.component._get_field_attr("welcome", "default"), "new value"
|
129
|
+
)
|
130
|
+
self.assertEqual(getattr(self.component, "welcome"), "hello world again")
|
131
|
+
|
132
|
+
def test_validation_error_handling(self):
|
133
|
+
"""Test handling of validation errors."""
|
134
|
+
with self.assertRaises(LionValueError):
|
135
|
+
Component.from_obj({"ln_id": 12345}) # Invalid ln_id type
|
136
|
+
|
137
|
+
def test_str_repr_methods(self):
|
138
|
+
"""Test __str__ and __repr__ methods."""
|
139
|
+
self.component.content = "example content"
|
140
|
+
self.assertIn("example content", str(self.component))
|
141
|
+
self.assertIn("example content", repr(self.component))
|
142
|
+
|
143
|
+
def test_embedded_content(self):
|
144
|
+
"""Test embedded content handling."""
|
145
|
+
embedding_str = "[1.0, 2.0, 3.0]"
|
146
|
+
self.component.embedding = self.component._validate_embedding(embedding_str)
|
147
|
+
self.assertEqual(self.component.embedding, [1.0, 2.0, 3.0])
|
148
|
+
|
149
|
+
def test_invalid_embedded_content(self):
|
150
|
+
"""Test invalid embedded content handling."""
|
151
|
+
with self.assertRaises(ValueError):
|
152
|
+
self.component._validate_embedding("[1.0, 'invalid', 3.0]")
|
153
|
+
|
154
|
+
def test_timestamp_format(self):
|
155
|
+
"""Test if the timestamp is in the correct format."""
|
156
|
+
timestamp = self.component.timestamp
|
157
|
+
try:
|
158
|
+
datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S.%f")
|
159
|
+
except ValueError:
|
160
|
+
self.fail("Timestamp is not in the correct format")
|
161
|
+
|
162
|
+
def test_default_field_values(self):
|
163
|
+
"""Test the default values of fields."""
|
164
|
+
default_fields = self.component._all_fields
|
165
|
+
self.assertEqual(default_fields["embedding"].default, [])
|
166
|
+
self.assertEqual(default_fields["metadata"].default_factory(), {})
|
167
|
+
self.assertEqual(default_fields["extra_fields"].default_factory(), {})
|
168
|
+
self.assertIsNone(default_fields["content"].default)
|
169
|
+
|
170
|
+
def test_deeply_nested_metadata(self):
|
171
|
+
"""Test setting and getting deeply nested metadata."""
|
172
|
+
nested_value = {"level1": {"level2": {"level3": "deep_value"}}}
|
173
|
+
self.component._meta_insert(["nested", 0], nested_value)
|
174
|
+
self.assertEqual(
|
175
|
+
self.component._meta_get(["nested", 0, "level1", "level2", "level3"]),
|
176
|
+
"deep_value",
|
177
|
+
)
|
178
|
+
|
179
|
+
def test_invalid_from_obj_type(self):
|
180
|
+
"""Test invalid type in from_obj method."""
|
181
|
+
with self.assertRaises(LionTypeError):
|
182
|
+
Component.from_obj(12345) # Invalid type
|
183
|
+
|
184
|
+
def test_from_obj_pydantic_model(self):
|
185
|
+
"""Test creating a Component from a Pydantic BaseModel."""
|
186
|
+
from pydantic import BaseModel
|
187
|
+
|
188
|
+
class SampleModel(BaseModel):
|
189
|
+
name: str
|
190
|
+
age: int
|
191
|
+
|
192
|
+
sample_instance = SampleModel(name="John Doe", age=30)
|
193
|
+
new_component = Component.from_obj(sample_instance)
|
194
|
+
self.assertEqual(new_component.metadata["name"], "John Doe")
|
195
|
+
self.assertEqual(new_component.metadata["age"], 30)
|
196
|
+
|
197
|
+
def test_json_serialization_deserialization(self):
|
198
|
+
"""Test JSON serialization and deserialization."""
|
199
|
+
original_dict = self.component.to_dict()
|
200
|
+
json_str = json.dumps(original_dict)
|
201
|
+
new_component = Component.from_obj(json_str)
|
202
|
+
self.assertEqual(original_dict, new_component.to_dict())
|
203
|
+
|
204
|
+
|
205
|
+
if __name__ == "__main__":
|
206
|
+
unittest.main()
|
@@ -0,0 +1,138 @@
|
|
1
|
+
import unittest
|
2
|
+
from lionagi import Node, pile, progression
|
3
|
+
from lionagi.core.collections.abc import Element
|
4
|
+
from lionagi.core.collections import Exchange
|
5
|
+
|
6
|
+
|
7
|
+
class TestExchange(unittest.TestCase):
|
8
|
+
|
9
|
+
def setUp(self):
|
10
|
+
# Setup for creating initial nodes, piles, and progressions
|
11
|
+
self.nodes = [Node(content=i) for i in range(10)]
|
12
|
+
self.pile1 = pile(self.nodes)
|
13
|
+
self.prog1 = progression(self.nodes[:5], name="left")
|
14
|
+
self.prog2 = progression(self.nodes[5:], name="right")
|
15
|
+
self.exchange = Exchange()
|
16
|
+
|
17
|
+
def test_initialization(self):
|
18
|
+
self.assertEqual(len(self.exchange.pile), 0)
|
19
|
+
self.assertEqual(len(self.exchange.pending_ins), 0)
|
20
|
+
self.assertEqual(len(self.exchange.pending_outs), 0)
|
21
|
+
|
22
|
+
def test_include_in(self):
|
23
|
+
sender_id = "sender1"
|
24
|
+
for node in self.nodes[:5]:
|
25
|
+
node.sender = sender_id
|
26
|
+
self.exchange.include(node, "in")
|
27
|
+
|
28
|
+
self.assertEqual(len(self.exchange.pile), 5)
|
29
|
+
self.assertIn(sender_id, self.exchange.pending_ins)
|
30
|
+
self.assertEqual(len(self.exchange.pending_ins[sender_id]), 5)
|
31
|
+
|
32
|
+
def test_include_out(self):
|
33
|
+
for node in self.nodes[:5]:
|
34
|
+
self.exchange.include(node, "out")
|
35
|
+
|
36
|
+
self.assertEqual(len(self.exchange.pile), 5)
|
37
|
+
self.assertEqual(len(self.exchange.pending_outs), 5)
|
38
|
+
|
39
|
+
def test_exclude(self):
|
40
|
+
for node in self.nodes[:5]:
|
41
|
+
self.exchange.include(node, "out")
|
42
|
+
|
43
|
+
node_to_remove = self.nodes[0]
|
44
|
+
self.exchange.exclude(node_to_remove)
|
45
|
+
self.assertNotIn(node_to_remove, self.exchange.pile)
|
46
|
+
self.assertNotIn(node_to_remove, self.exchange.pending_outs)
|
47
|
+
|
48
|
+
def test_exclude_from_in(self):
|
49
|
+
sender_id = "sender1"
|
50
|
+
for node in self.nodes[:5]:
|
51
|
+
node.sender = sender_id
|
52
|
+
self.exchange.include(node, "in")
|
53
|
+
|
54
|
+
node_to_remove = self.nodes[0]
|
55
|
+
self.exchange.exclude(node_to_remove)
|
56
|
+
self.assertNotIn(node_to_remove, self.exchange.pile)
|
57
|
+
self.assertNotIn(node_to_remove, self.exchange.pending_ins[sender_id])
|
58
|
+
|
59
|
+
def test_senders(self):
|
60
|
+
sender_id = "sender1"
|
61
|
+
for node in self.nodes[:5]:
|
62
|
+
node.sender = sender_id
|
63
|
+
self.exchange.include(node, "in")
|
64
|
+
|
65
|
+
self.assertIn(sender_id, self.exchange.senders)
|
66
|
+
|
67
|
+
def test_to_dict(self):
|
68
|
+
for node in self.nodes[:5]:
|
69
|
+
self.exchange.include(node, "out")
|
70
|
+
|
71
|
+
exchange_dict = self.exchange.to_dict()
|
72
|
+
self.assertIn("pile", exchange_dict)
|
73
|
+
self.assertIn("pending_ins", exchange_dict)
|
74
|
+
self.assertIn("pending_outs", exchange_dict)
|
75
|
+
|
76
|
+
def test_bool(self):
|
77
|
+
self.assertTrue(self.exchange)
|
78
|
+
|
79
|
+
# Additional Tests for Edge Cases
|
80
|
+
def test_include_non_sendable(self):
|
81
|
+
non_sendable = Element()
|
82
|
+
with self.assertRaises(AttributeError):
|
83
|
+
self.exchange.include(non_sendable, "in")
|
84
|
+
|
85
|
+
def test_exclude_nonexistent_item(self):
|
86
|
+
non_existent_node = Node(content="nonexistent")
|
87
|
+
result = self.exchange.exclude(non_existent_node)
|
88
|
+
self.assertTrue(result)
|
89
|
+
|
90
|
+
def test_include_multiple_items_in(self):
|
91
|
+
sender_id = "sender2"
|
92
|
+
new_nodes = [Node(content="new_node1"), Node(content="new_node2")]
|
93
|
+
for new_node in new_nodes:
|
94
|
+
new_node.sender = sender_id
|
95
|
+
self.exchange.include(new_node, "in")
|
96
|
+
|
97
|
+
self.assertEqual(len(self.exchange.pile), 2)
|
98
|
+
self.assertEqual(len(self.exchange.pending_ins[sender_id]), 2)
|
99
|
+
|
100
|
+
def test_include_multiple_items_out(self):
|
101
|
+
new_nodes = [Node(content="new_node1"), Node(content="new_node2")]
|
102
|
+
for new_node in new_nodes:
|
103
|
+
self.exchange.include(new_node, "out")
|
104
|
+
|
105
|
+
self.assertEqual(len(self.exchange.pile), 2)
|
106
|
+
self.assertEqual(len(self.exchange.pending_outs), 2)
|
107
|
+
|
108
|
+
def test_exclude_from_empty_exchange(self):
|
109
|
+
result = self.exchange.exclude(Node(content="nonexistent"))
|
110
|
+
self.assertTrue(result)
|
111
|
+
|
112
|
+
def test_clear_empty_exchange(self):
|
113
|
+
self.exchange.pile.clear()
|
114
|
+
self.exchange.pending_ins.clear()
|
115
|
+
self.exchange.pending_outs.clear()
|
116
|
+
self.assertEqual(len(self.exchange.pile), 0)
|
117
|
+
self.assertEqual(len(self.exchange.pending_ins), 0)
|
118
|
+
self.assertEqual(len(self.exchange.pending_outs), 0)
|
119
|
+
|
120
|
+
def test_clear_exchange(self):
|
121
|
+
sender_id = "sender3"
|
122
|
+
for node in self.nodes[:5]:
|
123
|
+
node.sender = sender_id
|
124
|
+
self.exchange.include(node, "in")
|
125
|
+
for node in self.nodes[5:]:
|
126
|
+
self.exchange.include(node, "out")
|
127
|
+
|
128
|
+
self.exchange.pile.clear()
|
129
|
+
self.exchange.pending_ins.clear()
|
130
|
+
self.exchange.pending_outs.clear()
|
131
|
+
|
132
|
+
self.assertEqual(len(self.exchange.pile), 0)
|
133
|
+
self.assertEqual(len(self.exchange.pending_ins), 0)
|
134
|
+
self.assertEqual(len(self.exchange.pending_outs), 0)
|
135
|
+
|
136
|
+
|
137
|
+
if __name__ == "__main__":
|
138
|
+
unittest.main()
|
@@ -0,0 +1,145 @@
|
|
1
|
+
import unittest
|
2
|
+
from lionagi import Node, pile, progression, flow
|
3
|
+
|
4
|
+
|
5
|
+
class TestFlow(unittest.TestCase):
|
6
|
+
|
7
|
+
def setUp(self):
|
8
|
+
# Setup for creating initial nodes, piles, and progressions
|
9
|
+
self.nodes = [Node(content=i) for i in range(10)]
|
10
|
+
self.pile1 = pile(self.nodes)
|
11
|
+
self.prog1 = progression(self.nodes[:5], name="left")
|
12
|
+
self.prog2 = progression(self.nodes[5:], name="right")
|
13
|
+
self.flow = flow([self.prog1, self.prog2])
|
14
|
+
|
15
|
+
def test_initialization(self):
|
16
|
+
self.assertEqual(len(self.flow.sequences), 2)
|
17
|
+
self.assertIn("left", self.flow.registry)
|
18
|
+
self.assertIn("right", self.flow.registry)
|
19
|
+
|
20
|
+
def test_append_to_flow(self):
|
21
|
+
new_node = Node(content="new_node")
|
22
|
+
self.flow.append(new_node, "left")
|
23
|
+
self.assertIn(new_node.ln_id, self.flow.get("left"))
|
24
|
+
|
25
|
+
def test_exclude_from_flow(self):
|
26
|
+
node_to_remove = self.nodes[0]
|
27
|
+
self.flow.exclude("left", node_to_remove)
|
28
|
+
self.assertNotIn(node_to_remove.ln_id, self.flow.get("left"))
|
29
|
+
|
30
|
+
def test_get_sequence(self):
|
31
|
+
seq = self.flow.get("left")
|
32
|
+
self.assertEqual(seq, self.prog1)
|
33
|
+
seq_default = self.flow.get("nonexistent", default=None)
|
34
|
+
self.assertIsNone(seq_default)
|
35
|
+
|
36
|
+
def test_register_sequence(self):
|
37
|
+
new_prog = progression(self.nodes[2:4], name="middle")
|
38
|
+
self.flow.register(new_prog)
|
39
|
+
self.assertIn("middle", self.flow.registry)
|
40
|
+
|
41
|
+
def test_popleft(self):
|
42
|
+
first_node_id = self.prog1[0]
|
43
|
+
removed_node_id = self.flow.popleft("left")
|
44
|
+
self.assertEqual(first_node_id, removed_node_id)
|
45
|
+
self.assertNotIn(removed_node_id, self.flow.get("left"))
|
46
|
+
|
47
|
+
def test_shape_and_size(self):
|
48
|
+
shape = self.flow.shape()
|
49
|
+
self.assertEqual(shape["left"], 5)
|
50
|
+
self.assertEqual(shape["right"], 5)
|
51
|
+
size = self.flow.size()
|
52
|
+
self.assertEqual(size, 10)
|
53
|
+
|
54
|
+
def test_clear(self):
|
55
|
+
self.flow.clear()
|
56
|
+
self.assertEqual(len(self.flow.sequences), 0)
|
57
|
+
self.assertEqual(len(self.flow.registry), 0)
|
58
|
+
|
59
|
+
def test_iteration(self):
|
60
|
+
items = list(self.flow)
|
61
|
+
self.assertEqual(len(items), 2)
|
62
|
+
|
63
|
+
def test_to_df(self):
|
64
|
+
df = self.flow.to_df()
|
65
|
+
self.assertEqual(df.shape[0], 2)
|
66
|
+
self.assertEqual(df.columns.tolist(), ["order", "name"])
|
67
|
+
|
68
|
+
def test_inclusion_check(self):
|
69
|
+
self.assertTrue(self.prog1 in self.flow)
|
70
|
+
self.assertTrue(self.prog2 in self.flow)
|
71
|
+
try:
|
72
|
+
a = Node(content="nonexistent") in self.flow
|
73
|
+
except TypeError:
|
74
|
+
pass
|
75
|
+
|
76
|
+
# Additional Tests for Edge Cases
|
77
|
+
def test_empty_flow_initialization(self):
|
78
|
+
empty_flow = flow()
|
79
|
+
self.assertEqual(len(empty_flow.sequences), 0)
|
80
|
+
self.assertEqual(len(empty_flow.registry), 0)
|
81
|
+
|
82
|
+
def test_append_to_nonexistent_sequence(self):
|
83
|
+
new_node = Node(content="new_node")
|
84
|
+
self.flow.append(new_node, "nonexistent")
|
85
|
+
self.assertIn(new_node.ln_id, self.flow.get("nonexistent"))
|
86
|
+
|
87
|
+
def test_exclude_nonexistent_item(self):
|
88
|
+
non_existent_node = Node(content="nonexistent")
|
89
|
+
result = self.flow.exclude("left", non_existent_node)
|
90
|
+
self.assertTrue(result)
|
91
|
+
|
92
|
+
def test_exclude_nonexistent_sequence(self):
|
93
|
+
result = self.flow.exclude("nonexistent")
|
94
|
+
self.assertFalse(result)
|
95
|
+
|
96
|
+
def test_get_nonexistent_sequence(self):
|
97
|
+
try:
|
98
|
+
self.flow.get("nonexistent")
|
99
|
+
except Exception as e:
|
100
|
+
if e.__class__.__name__ == "LionTypeError":
|
101
|
+
return
|
102
|
+
raise AssertionError("LionTypeError not raised")
|
103
|
+
|
104
|
+
def test_register_existing_sequence_name(self):
|
105
|
+
with self.assertRaises(ValueError):
|
106
|
+
self.flow.register(self.prog1, name="right")
|
107
|
+
|
108
|
+
def test_popleft_nonexistent_sequence(self):
|
109
|
+
try:
|
110
|
+
self.flow.popleft("nonexistent")
|
111
|
+
except Exception as e:
|
112
|
+
if e.__class__.__name__ == "LionTypeError":
|
113
|
+
return
|
114
|
+
raise AssertionError("LionTypeError not raised")
|
115
|
+
|
116
|
+
def test_append_multiple_items(self):
|
117
|
+
new_nodes = [Node(content="new_node1"), Node(content="new_node2")]
|
118
|
+
for new_node in new_nodes:
|
119
|
+
self.flow.append(new_node, "left")
|
120
|
+
for new_node in new_nodes:
|
121
|
+
self.assertIn(new_node.ln_id, self.flow.get("left"))
|
122
|
+
|
123
|
+
def test_remove_from_all_sequences(self):
|
124
|
+
node_to_remove = self.nodes[0]
|
125
|
+
self.flow.append(node_to_remove, "right")
|
126
|
+
self.flow.remove(node_to_remove)
|
127
|
+
self.assertNotIn(node_to_remove.ln_id, self.flow.get("right"))
|
128
|
+
|
129
|
+
def test_shape_empty_flow(self):
|
130
|
+
empty_flow = flow()
|
131
|
+
self.assertEqual(empty_flow.shape(), {})
|
132
|
+
|
133
|
+
def test_size_empty_flow(self):
|
134
|
+
empty_flow = flow()
|
135
|
+
self.assertEqual(empty_flow.size(), 0)
|
136
|
+
|
137
|
+
def test_clear_empty_flow(self):
|
138
|
+
empty_flow = flow()
|
139
|
+
empty_flow.clear()
|
140
|
+
self.assertEqual(len(empty_flow.sequences), 0)
|
141
|
+
self.assertEqual(len(empty_flow.registry), 0)
|
142
|
+
|
143
|
+
|
144
|
+
if __name__ == "__main__":
|
145
|
+
unittest.main()
|