lionagi 0.1.2__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.
Files changed (268) hide show
  1. lionagi/__init__.py +60 -5
  2. lionagi/core/__init__.py +0 -25
  3. lionagi/core/_setting/_setting.py +59 -0
  4. lionagi/core/action/__init__.py +14 -0
  5. lionagi/core/action/function_calling.py +136 -0
  6. lionagi/core/action/manual.py +1 -0
  7. lionagi/core/action/node.py +109 -0
  8. lionagi/core/action/tool.py +114 -0
  9. lionagi/core/action/tool_manager.py +356 -0
  10. lionagi/core/agent/base_agent.py +27 -13
  11. lionagi/core/agent/eval/evaluator.py +1 -0
  12. lionagi/core/agent/eval/vote.py +40 -0
  13. lionagi/core/agent/learn/learner.py +59 -0
  14. lionagi/core/agent/plan/unit_template.py +1 -0
  15. lionagi/core/collections/__init__.py +17 -0
  16. lionagi/core/{generic/data_logger.py → collections/_logger.py} +69 -55
  17. lionagi/core/collections/abc/__init__.py +53 -0
  18. lionagi/core/collections/abc/component.py +615 -0
  19. lionagi/core/collections/abc/concepts.py +297 -0
  20. lionagi/core/collections/abc/exceptions.py +150 -0
  21. lionagi/core/collections/abc/util.py +45 -0
  22. lionagi/core/collections/exchange.py +161 -0
  23. lionagi/core/collections/flow.py +426 -0
  24. lionagi/core/collections/model.py +419 -0
  25. lionagi/core/collections/pile.py +913 -0
  26. lionagi/core/collections/progression.py +236 -0
  27. lionagi/core/collections/util.py +64 -0
  28. lionagi/core/director/direct.py +314 -0
  29. lionagi/core/director/director.py +2 -0
  30. lionagi/core/{execute/branch_executor.py → engine/branch_engine.py} +134 -97
  31. lionagi/core/{execute/instruction_map_executor.py → engine/instruction_map_engine.py} +80 -55
  32. lionagi/{experimental/directive/evaluator → core/engine}/script_engine.py +17 -1
  33. lionagi/core/executor/base_executor.py +90 -0
  34. lionagi/core/{execute/structure_executor.py → executor/graph_executor.py} +62 -66
  35. lionagi/core/{execute → executor}/neo4j_executor.py +70 -67
  36. lionagi/core/generic/__init__.py +3 -33
  37. lionagi/core/generic/edge.py +29 -79
  38. lionagi/core/generic/edge_condition.py +16 -0
  39. lionagi/core/generic/graph.py +236 -0
  40. lionagi/core/generic/hyperedge.py +1 -0
  41. lionagi/core/generic/node.py +156 -221
  42. lionagi/core/generic/tree.py +48 -0
  43. lionagi/core/generic/tree_node.py +79 -0
  44. lionagi/core/mail/__init__.py +12 -0
  45. lionagi/core/mail/mail.py +25 -0
  46. lionagi/core/mail/mail_manager.py +139 -58
  47. lionagi/core/mail/package.py +45 -0
  48. lionagi/core/mail/start_mail.py +36 -0
  49. lionagi/core/message/__init__.py +19 -0
  50. lionagi/core/message/action_request.py +133 -0
  51. lionagi/core/message/action_response.py +135 -0
  52. lionagi/core/message/assistant_response.py +95 -0
  53. lionagi/core/message/instruction.py +234 -0
  54. lionagi/core/message/message.py +101 -0
  55. lionagi/core/message/system.py +86 -0
  56. lionagi/core/message/util.py +283 -0
  57. lionagi/core/report/__init__.py +4 -0
  58. lionagi/core/report/base.py +217 -0
  59. lionagi/core/report/form.py +231 -0
  60. lionagi/core/report/report.py +166 -0
  61. lionagi/core/report/util.py +28 -0
  62. lionagi/core/rule/_default.py +16 -0
  63. lionagi/core/rule/action.py +99 -0
  64. lionagi/core/rule/base.py +238 -0
  65. lionagi/core/rule/boolean.py +56 -0
  66. lionagi/core/rule/choice.py +47 -0
  67. lionagi/core/rule/mapping.py +96 -0
  68. lionagi/core/rule/number.py +71 -0
  69. lionagi/core/rule/rulebook.py +109 -0
  70. lionagi/core/rule/string.py +52 -0
  71. lionagi/core/rule/util.py +35 -0
  72. lionagi/core/session/branch.py +431 -0
  73. lionagi/core/session/directive_mixin.py +287 -0
  74. lionagi/core/session/session.py +229 -903
  75. lionagi/core/structure/__init__.py +1 -0
  76. lionagi/core/structure/chain.py +1 -0
  77. lionagi/core/structure/forest.py +1 -0
  78. lionagi/core/structure/graph.py +1 -0
  79. lionagi/core/structure/tree.py +1 -0
  80. lionagi/core/unit/__init__.py +5 -0
  81. lionagi/core/unit/parallel_unit.py +245 -0
  82. lionagi/core/unit/template/action.py +81 -0
  83. lionagi/core/unit/template/base.py +51 -0
  84. lionagi/core/unit/template/plan.py +84 -0
  85. lionagi/core/unit/template/predict.py +109 -0
  86. lionagi/core/unit/template/score.py +124 -0
  87. lionagi/core/unit/template/select.py +104 -0
  88. lionagi/core/unit/unit.py +362 -0
  89. lionagi/core/unit/unit_form.py +305 -0
  90. lionagi/core/unit/unit_mixin.py +1168 -0
  91. lionagi/core/unit/util.py +71 -0
  92. lionagi/core/validator/validator.py +364 -0
  93. lionagi/core/work/work.py +76 -0
  94. lionagi/core/work/work_function.py +101 -0
  95. lionagi/core/work/work_queue.py +103 -0
  96. lionagi/core/work/worker.py +258 -0
  97. lionagi/core/work/worklog.py +120 -0
  98. lionagi/experimental/compressor/base.py +46 -0
  99. lionagi/experimental/compressor/llm_compressor.py +247 -0
  100. lionagi/experimental/compressor/llm_summarizer.py +61 -0
  101. lionagi/experimental/compressor/util.py +70 -0
  102. lionagi/experimental/directive/__init__.py +19 -0
  103. lionagi/experimental/directive/parser/base_parser.py +69 -2
  104. lionagi/experimental/directive/{template_ → template}/base_template.py +17 -1
  105. lionagi/{libs/ln_tokenizer.py → experimental/directive/tokenizer.py} +16 -0
  106. lionagi/experimental/{directive/evaluator → evaluator}/ast_evaluator.py +16 -0
  107. lionagi/experimental/{directive/evaluator → evaluator}/base_evaluator.py +16 -0
  108. lionagi/experimental/knowledge/base.py +10 -0
  109. lionagi/experimental/memory/__init__.py +0 -0
  110. lionagi/experimental/strategies/__init__.py +0 -0
  111. lionagi/experimental/strategies/base.py +1 -0
  112. lionagi/integrations/bridge/langchain_/documents.py +4 -0
  113. lionagi/integrations/bridge/llamaindex_/index.py +30 -0
  114. lionagi/integrations/bridge/llamaindex_/llama_index_bridge.py +6 -0
  115. lionagi/integrations/chunker/chunk.py +161 -24
  116. lionagi/integrations/config/oai_configs.py +34 -3
  117. lionagi/integrations/config/openrouter_configs.py +14 -2
  118. lionagi/integrations/loader/load.py +122 -21
  119. lionagi/integrations/loader/load_util.py +6 -77
  120. lionagi/integrations/provider/_mapping.py +46 -0
  121. lionagi/integrations/provider/litellm.py +2 -1
  122. lionagi/integrations/provider/mlx_service.py +16 -9
  123. lionagi/integrations/provider/oai.py +91 -4
  124. lionagi/integrations/provider/ollama.py +6 -5
  125. lionagi/integrations/provider/openrouter.py +115 -8
  126. lionagi/integrations/provider/services.py +2 -2
  127. lionagi/integrations/provider/transformers.py +18 -22
  128. lionagi/integrations/storage/__init__.py +3 -3
  129. lionagi/integrations/storage/neo4j.py +52 -60
  130. lionagi/integrations/storage/storage_util.py +44 -46
  131. lionagi/integrations/storage/structure_excel.py +43 -26
  132. lionagi/integrations/storage/to_excel.py +11 -4
  133. lionagi/libs/__init__.py +22 -1
  134. lionagi/libs/ln_api.py +75 -20
  135. lionagi/libs/ln_context.py +37 -0
  136. lionagi/libs/ln_convert.py +21 -9
  137. lionagi/libs/ln_func_call.py +69 -28
  138. lionagi/libs/ln_image.py +107 -0
  139. lionagi/libs/ln_nested.py +26 -11
  140. lionagi/libs/ln_parse.py +82 -23
  141. lionagi/libs/ln_queue.py +16 -0
  142. lionagi/libs/ln_tokenize.py +164 -0
  143. lionagi/libs/ln_validate.py +16 -0
  144. lionagi/libs/special_tokens.py +172 -0
  145. lionagi/libs/sys_util.py +95 -24
  146. lionagi/lions/coder/code_form.py +13 -0
  147. lionagi/lions/coder/coder.py +50 -3
  148. lionagi/lions/coder/util.py +30 -25
  149. lionagi/tests/libs/test_func_call.py +23 -21
  150. lionagi/tests/libs/test_nested.py +36 -21
  151. lionagi/tests/libs/test_parse.py +1 -1
  152. lionagi/tests/test_core/collections/__init__.py +0 -0
  153. lionagi/tests/test_core/collections/test_component.py +206 -0
  154. lionagi/tests/test_core/collections/test_exchange.py +138 -0
  155. lionagi/tests/test_core/collections/test_flow.py +145 -0
  156. lionagi/tests/test_core/collections/test_pile.py +171 -0
  157. lionagi/tests/test_core/collections/test_progression.py +129 -0
  158. lionagi/tests/test_core/generic/test_edge.py +67 -0
  159. lionagi/tests/test_core/generic/test_graph.py +96 -0
  160. lionagi/tests/test_core/generic/test_node.py +106 -0
  161. lionagi/tests/test_core/generic/test_tree_node.py +73 -0
  162. lionagi/tests/test_core/test_branch.py +115 -294
  163. lionagi/tests/test_core/test_form.py +46 -0
  164. lionagi/tests/test_core/test_report.py +105 -0
  165. lionagi/tests/test_core/test_validator.py +111 -0
  166. lionagi/version.py +1 -1
  167. lionagi-0.2.1.dist-info/LICENSE +202 -0
  168. lionagi-0.2.1.dist-info/METADATA +272 -0
  169. lionagi-0.2.1.dist-info/RECORD +240 -0
  170. lionagi/core/branch/base.py +0 -653
  171. lionagi/core/branch/branch.py +0 -474
  172. lionagi/core/branch/flow_mixin.py +0 -96
  173. lionagi/core/branch/util.py +0 -323
  174. lionagi/core/direct/__init__.py +0 -19
  175. lionagi/core/direct/cot.py +0 -123
  176. lionagi/core/direct/plan.py +0 -164
  177. lionagi/core/direct/predict.py +0 -166
  178. lionagi/core/direct/react.py +0 -171
  179. lionagi/core/direct/score.py +0 -279
  180. lionagi/core/direct/select.py +0 -170
  181. lionagi/core/direct/sentiment.py +0 -1
  182. lionagi/core/direct/utils.py +0 -110
  183. lionagi/core/direct/vote.py +0 -64
  184. lionagi/core/execute/base_executor.py +0 -47
  185. lionagi/core/flow/baseflow.py +0 -23
  186. lionagi/core/flow/monoflow/ReAct.py +0 -240
  187. lionagi/core/flow/monoflow/__init__.py +0 -9
  188. lionagi/core/flow/monoflow/chat.py +0 -95
  189. lionagi/core/flow/monoflow/chat_mixin.py +0 -253
  190. lionagi/core/flow/monoflow/followup.py +0 -215
  191. lionagi/core/flow/polyflow/__init__.py +0 -1
  192. lionagi/core/flow/polyflow/chat.py +0 -251
  193. lionagi/core/form/action_form.py +0 -26
  194. lionagi/core/form/field_validator.py +0 -287
  195. lionagi/core/form/form.py +0 -302
  196. lionagi/core/form/mixin.py +0 -214
  197. lionagi/core/form/scored_form.py +0 -13
  198. lionagi/core/generic/action.py +0 -26
  199. lionagi/core/generic/component.py +0 -532
  200. lionagi/core/generic/condition.py +0 -46
  201. lionagi/core/generic/mail.py +0 -90
  202. lionagi/core/generic/mailbox.py +0 -36
  203. lionagi/core/generic/relation.py +0 -70
  204. lionagi/core/generic/signal.py +0 -22
  205. lionagi/core/generic/structure.py +0 -362
  206. lionagi/core/generic/transfer.py +0 -20
  207. lionagi/core/generic/work.py +0 -40
  208. lionagi/core/graph/graph.py +0 -126
  209. lionagi/core/graph/tree.py +0 -190
  210. lionagi/core/mail/schema.py +0 -63
  211. lionagi/core/messages/schema.py +0 -325
  212. lionagi/core/tool/__init__.py +0 -5
  213. lionagi/core/tool/tool.py +0 -28
  214. lionagi/core/tool/tool_manager.py +0 -283
  215. lionagi/experimental/report/form.py +0 -64
  216. lionagi/experimental/report/report.py +0 -138
  217. lionagi/experimental/report/util.py +0 -47
  218. lionagi/experimental/tool/function_calling.py +0 -43
  219. lionagi/experimental/tool/manual.py +0 -66
  220. lionagi/experimental/tool/schema.py +0 -59
  221. lionagi/experimental/tool/tool_manager.py +0 -138
  222. lionagi/experimental/tool/util.py +0 -16
  223. lionagi/experimental/validator/rule.py +0 -139
  224. lionagi/experimental/validator/validator.py +0 -56
  225. lionagi/experimental/work/__init__.py +0 -10
  226. lionagi/experimental/work/async_queue.py +0 -54
  227. lionagi/experimental/work/schema.py +0 -73
  228. lionagi/experimental/work/work_function.py +0 -67
  229. lionagi/experimental/work/worker.py +0 -56
  230. lionagi/experimental/work2/form.py +0 -371
  231. lionagi/experimental/work2/report.py +0 -289
  232. lionagi/experimental/work2/schema.py +0 -30
  233. lionagi/experimental/work2/tests.py +0 -72
  234. lionagi/experimental/work2/work_function.py +0 -89
  235. lionagi/experimental/work2/worker.py +0 -12
  236. lionagi/integrations/bridge/llamaindex_/get_index.py +0 -294
  237. lionagi/tests/test_core/generic/test_component.py +0 -89
  238. lionagi/tests/test_core/test_base_branch.py +0 -426
  239. lionagi/tests/test_core/test_chat_flow.py +0 -63
  240. lionagi/tests/test_core/test_mail_manager.py +0 -75
  241. lionagi/tests/test_core/test_prompts.py +0 -51
  242. lionagi/tests/test_core/test_session.py +0 -254
  243. lionagi/tests/test_core/test_session_base_util.py +0 -313
  244. lionagi/tests/test_core/test_tool_manager.py +0 -95
  245. lionagi-0.1.2.dist-info/LICENSE +0 -9
  246. lionagi-0.1.2.dist-info/METADATA +0 -174
  247. lionagi-0.1.2.dist-info/RECORD +0 -206
  248. /lionagi/core/{branch → _setting}/__init__.py +0 -0
  249. /lionagi/core/{execute → agent/eval}/__init__.py +0 -0
  250. /lionagi/core/{flow → agent/learn}/__init__.py +0 -0
  251. /lionagi/core/{form → agent/plan}/__init__.py +0 -0
  252. /lionagi/core/{branch/executable_branch.py → agent/plan/plan.py} +0 -0
  253. /lionagi/core/{graph → director}/__init__.py +0 -0
  254. /lionagi/core/{messages → engine}/__init__.py +0 -0
  255. /lionagi/{experimental/directive/evaluator → core/engine}/sandbox_.py +0 -0
  256. /lionagi/{experimental/directive/evaluator → core/executor}/__init__.py +0 -0
  257. /lionagi/{experimental/directive/template_ → core/rule}/__init__.py +0 -0
  258. /lionagi/{experimental/report → core/unit/template}/__init__.py +0 -0
  259. /lionagi/{experimental/tool → core/validator}/__init__.py +0 -0
  260. /lionagi/{experimental/validator → core/work}/__init__.py +0 -0
  261. /lionagi/experimental/{work2 → compressor}/__init__.py +0 -0
  262. /lionagi/{core/flow/mono_chat_mixin.py → experimental/directive/template/__init__.py} +0 -0
  263. /lionagi/experimental/directive/{schema.py → template/schema.py} +0 -0
  264. /lionagi/experimental/{work2/util.py → evaluator/__init__.py} +0 -0
  265. /lionagi/experimental/{work2/work.py → knowledge/__init__.py} +0 -0
  266. /lionagi/{tests/libs/test_async.py → experimental/knowledge/graph.py} +0 -0
  267. {lionagi-0.1.2.dist-info → lionagi-0.2.1.dist-info}/WHEEL +0 -0
  268. {lionagi-0.1.2.dist-info → lionagi-0.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,426 @@
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 collections.abc import Mapping
18
+ from collections import deque
19
+ from typing import Tuple
20
+ from pydantic import Field
21
+ import contextlib
22
+ from .abc import (
23
+ Record,
24
+ LionTypeError,
25
+ ItemNotFoundError,
26
+ LionIDable,
27
+ Element,
28
+ )
29
+ from .pile import Pile, pile
30
+ from .progression import Progression, progression
31
+
32
+
33
+ class Flow(Element):
34
+ """
35
+ Represents a flow of categorical sequences.
36
+
37
+ Attributes:
38
+ sequences (Pile[Progression]): A collection of progression sequences.
39
+ registry (dict[str, str]): A registry mapping sequence names to IDs.
40
+ default_name (str): The default name for the flow.
41
+ """
42
+
43
+ sequences: Pile[Progression] = Field(
44
+ default_factory=lambda: pile({}, Progression, use_obj=True)
45
+ )
46
+
47
+ registry: dict[str, str] = {}
48
+ default_name: str = "main"
49
+
50
+ def __init__(self, sequences=None, default_name=None):
51
+ """
52
+ Initializes a Flow instance.
53
+
54
+ Args:
55
+ sequences (optional): Initial sequences to include in the flow.
56
+ default_name (optional): Default name for the flow.
57
+ """
58
+ super().__init__()
59
+ self.sequences = self._validate_sequences(sequences)
60
+ self.default_name = default_name or "main"
61
+
62
+ def _validate_sequences(self, value):
63
+ """
64
+ Validates and initializes the sequences.
65
+
66
+ Args:
67
+ value: Sequences to validate and initialize.
68
+
69
+ Returns:
70
+ Pile[Progression]: A pile of progression sequences.
71
+ """
72
+ if not value:
73
+ return pile({}, Progression, use_obj=True)
74
+ if isinstance(value, dict):
75
+ return pile(value, Progression, use_obj=True)
76
+ if (
77
+ isinstance(value, list)
78
+ and len(value) > 0
79
+ and isinstance(value[0], Progression)
80
+ ):
81
+ return pile({i.ln_id: i for i in value}, Progression, use_obj=True)
82
+ return pile({}, Progression, use_obj=True)
83
+
84
+ def all_orders(self) -> list[list[str]]:
85
+ """
86
+ Retrieves all orders in the flow.
87
+
88
+ Returns:
89
+ list[list[str]]: A list of lists containing sequence orders.
90
+ """
91
+ return [list(seq) for seq in self.sequences]
92
+
93
+ def all_unique_items(self) -> Tuple[str]:
94
+ """
95
+ Retrieves all unique items across sequences.
96
+
97
+ Returns:
98
+ Tuple[str]: A tuple of unique items.
99
+ """
100
+ return tuple({item for seq in self.sequences for item in seq})
101
+
102
+ def keys(self):
103
+ yield from self.sequences.keys()
104
+
105
+ def values(self):
106
+ yield from self.sequences.values()
107
+
108
+ def items(self):
109
+ yield from self.sequences.items()
110
+
111
+ def get(self, seq=None, default=...):
112
+ """
113
+ Retrieves a sequence by name or returns the default sequence.
114
+
115
+ Args:
116
+ seq (optional): The name of the sequence.
117
+ default (optional): Default value if sequence is not found.
118
+
119
+ Returns:
120
+ Progression: The requested sequence.
121
+ """
122
+
123
+ if seq is None:
124
+ if self.default_name in self.registry:
125
+ seq = self.registry[self.default_name]
126
+ seq = self.sequences[seq]
127
+ else:
128
+ raise ItemNotFoundError("No sequence found.")
129
+
130
+ elif seq is not None and seq in self:
131
+ if not isinstance(seq, (str, Progression)):
132
+ raise LionTypeError("Sequence must be of type Progression.")
133
+
134
+ if isinstance(seq, str):
135
+ seq = self.registry[seq]
136
+
137
+ return (
138
+ self.sequences[seq] if default == ... else self.sequences.get(seq, default)
139
+ )
140
+
141
+ def __getitem__(self, seq=None, /):
142
+ return self.get(seq)
143
+
144
+ def __setitem__(self, seq: LionIDable | str, index=None, value=None, /):
145
+ if seq not in self:
146
+ raise ItemNotFoundError(f"Sequence {seq}")
147
+
148
+ if index:
149
+ self.sequences[seq][index] = value
150
+ return
151
+
152
+ self.sequences[seq] = value
153
+
154
+ def __contains__(self, item):
155
+ return (
156
+ item in self.registry
157
+ or item in self.sequences
158
+ or item in self.all_unique_items()
159
+ )
160
+
161
+ def shape(self):
162
+ return (len(self.all_orders()), [len(i) for i in self.all_orders()])
163
+
164
+ def size(self):
165
+ return sum(len(seq) for seq in self.all_orders())
166
+
167
+ def clear(self):
168
+ self.sequences.clear()
169
+ self.registry.clear()
170
+
171
+ def include(self, seq=None, item=None, name=None):
172
+ _sequence = self._find_sequence(seq, None) or self._find_sequence(name, None)
173
+ if not _sequence:
174
+ if not item and not name:
175
+ """None is not in the registry or sequencees."""
176
+ return False
177
+ if item:
178
+ self.append(item, name)
179
+ return item in self
180
+
181
+ else:
182
+ if _sequence in self:
183
+ if not item and not name:
184
+ return True
185
+ if item:
186
+ self.append(item, _sequence)
187
+ return item in self
188
+ return True # will ignore name if sequence is already found
189
+
190
+ else:
191
+ if isinstance(seq, Progression):
192
+ if item and seq.include(item):
193
+ self.register(seq, name)
194
+ return seq in self
195
+
196
+ return False
197
+
198
+ def exclude(self, seq: LionIDable = None, item=None, name=None):
199
+ """
200
+ Excludes an item or sequence from the flow.
201
+
202
+ Args:
203
+ seq (LionIDable, optional): The sequence to exclude from.
204
+ item (optional): The item to exclude.
205
+ name (optional): The name of the sequence.
206
+
207
+ Returns:
208
+ bool: True if exclusion was successful, False otherwise.
209
+ """
210
+ # if sequence is not None, we will not check the name
211
+ if seq is not None:
212
+
213
+ with contextlib.suppress(ItemNotFoundError, AttributeError):
214
+ if item:
215
+ # if there is item, we exclude it from the sequence
216
+ self.sequences[self.registry[seq]].exclude(item)
217
+ return item not in self.sequences[self.registry[seq]]
218
+ else:
219
+ # if there is no item, we exclude the sequence
220
+ a = self.registry.pop(seq.name or seq.ln_id, None)
221
+ return a is not None and self.sequences.exclude(seq)
222
+ return False
223
+
224
+ elif name is not None:
225
+
226
+ with contextlib.suppress(ItemNotFoundError):
227
+ if item:
228
+ # if there is item, we exclude it from the sequence
229
+ return self.sequences[self.registry[name]].exclude(item)
230
+ else:
231
+ # if there is no item, we exclude the sequence
232
+ a = self.registry.pop(name, None)
233
+ return a is not None and self.sequences.exclude(a)
234
+ return False
235
+
236
+ def register(self, sequence: Progression, name: str = None):
237
+ """
238
+ Registers a sequence with a name.
239
+
240
+ Args:
241
+ sequence (Progression): The sequence to register.
242
+ name (str, optional): The name for the sequence.
243
+
244
+ Raises:
245
+ LionTypeError: If the sequence is not of type Progression.
246
+ ValueError: If the sequence name already exists.
247
+ """
248
+
249
+ if not isinstance(sequence, Progression):
250
+ raise LionTypeError(f"Sequence must be of type Progression.")
251
+
252
+ name = name or sequence.name
253
+ if not name:
254
+ if self.default_name in self.registry:
255
+ name = sequence.ln_id
256
+ else:
257
+ name = self.default_name
258
+
259
+ if name in self.registry:
260
+ raise ValueError(f"Sequence '{name}' already exists.")
261
+
262
+ self.sequences.include(sequence)
263
+ self.registry[name] = sequence.ln_id
264
+
265
+ def append(self, item, sequence=None, /):
266
+ """
267
+ Appends an item to a sequence.
268
+
269
+ Args:
270
+ item: The item to append.
271
+ sequence (optional): The sequence to append to.
272
+ """
273
+ if not sequence:
274
+ if self.default_name in self.registry:
275
+ sequence = self.registry[self.default_name]
276
+ self.sequences[sequence].include(item)
277
+ return
278
+
279
+ p = progression(item, self.default_name)
280
+ self.register(p)
281
+ return
282
+
283
+ if sequence in self.sequences:
284
+ self.sequences[sequence].include(item)
285
+ return
286
+
287
+ if sequence in self.registry:
288
+ self.sequences[self.registry[sequence]].include(item)
289
+ return
290
+
291
+ p = progression(item, sequence if isinstance(sequence, str) else None)
292
+ self.register(p)
293
+
294
+ def popleft(self, sequence=None, /):
295
+ """
296
+ Removes and returns an item from the left end of a sequence.
297
+
298
+ Args:
299
+ sequence (optional): The sequence to remove the item from.
300
+
301
+ Returns:
302
+ The removed item.
303
+ """
304
+ sequence = self._find_sequence(sequence)
305
+ return self.sequences[sequence].popleft()
306
+
307
+ def shape(self):
308
+ return {key: len(self.sequences[value]) for key, value in self.registry.items()}
309
+
310
+ def get(self, sequence: str, /, default=...) -> deque[str] | None:
311
+ sequence = getattr(sequence, "ln_id", None) or sequence
312
+
313
+ if sequence in self.registry:
314
+ return self.sequences[self.registry[sequence]]
315
+
316
+ try:
317
+ return self.sequences[sequence]
318
+ except KeyError as e:
319
+ if default == ...:
320
+ raise e
321
+ return default
322
+
323
+ def remove(self, item, sequence="all"):
324
+ """
325
+ Removes an item from a sequence or all sequences.
326
+
327
+ Args:
328
+ item: The item to remove.
329
+ sequence (str, optional): The sequence to remove the item from. Defaults to "all".
330
+ """
331
+ if sequence == "all":
332
+ for seq in self.sequences:
333
+ seq.remove(item)
334
+ return
335
+
336
+ sequence = self._find_sequence(sequence)
337
+ self.sequences[sequence].remove(item)
338
+
339
+ def __len__(self):
340
+ return len(self.sequences)
341
+
342
+ def __iter__(self):
343
+ return iter(self.sequences)
344
+
345
+ def __next__(self):
346
+ return next(self.__iter__())
347
+
348
+ def _find_sequence(self, sequence=None, default=...):
349
+ """
350
+ Finds the sequence ID in the registry or sequences.
351
+
352
+ Args:
353
+ sequence (optional): The sequence to find.
354
+ default (optional): The default value if sequence is not found.
355
+
356
+ Returns:
357
+ The found sequence ID.
358
+
359
+ Raises:
360
+ ItemNotFoundError: If no sequence is found.
361
+ """
362
+
363
+ if not sequence:
364
+ if self.default_name in self.registry:
365
+ return self.registry[self.default_name]
366
+ if default != ...:
367
+ return default
368
+ raise ItemNotFoundError("No sequence found.")
369
+
370
+ if sequence in self.sequences:
371
+ return sequence.ln_id if isinstance(sequence, Progression) else sequence
372
+
373
+ if sequence in self.registry:
374
+ return self.registry[sequence]
375
+
376
+ def to_dict(self):
377
+ return {
378
+ "sequences": self.sequences.to_dict(),
379
+ "registry": self.registry,
380
+ "default_name": self.default_name,
381
+ }
382
+
383
+ def to_df(self):
384
+ return self.sequences.to_df()
385
+
386
+
387
+ def flow(sequences=None, default_name=None, /):
388
+ """
389
+ Creates a new Flow instance.
390
+
391
+ Args:
392
+ sequences (optional): Initial sequences to include in the flow.
393
+ default_name (optional): Default name for the flow.
394
+
395
+ Returns:
396
+ Flow: A new Flow instance.
397
+ """
398
+ if sequences is None:
399
+ return Flow()
400
+
401
+ flow = Flow()
402
+ if default_name:
403
+ flow.default_name = default_name
404
+
405
+ # if mapping we assume a dictionary of in {name: data} format
406
+ if isinstance(sequences, (Mapping, Record)):
407
+ for name, seq in sequences.items():
408
+ if not isinstance(seq, Progression):
409
+ try:
410
+ seq = progression(seq, name)
411
+ except Exception as e:
412
+ raise e
413
+ if (a := name or seq.name) is not None:
414
+ flow.register(seq, a)
415
+ else:
416
+ flow.register(seq, seq.ln_id)
417
+ return flow
418
+
419
+ for seq in sequences:
420
+ if not isinstance(seq, Progression):
421
+ try:
422
+ seq = progression(seq)
423
+ except Exception as e:
424
+ raise e
425
+ flow.register(seq)
426
+ return flow