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.
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 +74 -0
  94. lionagi/core/work/work_function.py +92 -0
  95. lionagi/core/work/work_queue.py +81 -0
  96. lionagi/core/work/worker.py +195 -0
  97. lionagi/core/work/worklog.py +124 -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.0.dist-info/LICENSE +202 -0
  168. lionagi-0.2.0.dist-info/METADATA +272 -0
  169. lionagi-0.2.0.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.0.dist-info}/WHEEL +0 -0
  268. {lionagi-0.1.2.dist-info → lionagi-0.2.0.dist-info}/top_level.txt +0 -0
@@ -1,653 +0,0 @@
1
- from abc import ABC
2
- from typing import Any
3
-
4
- from pathlib import Path
5
- from lionagi.libs import convert, dataframe, SysUtil
6
-
7
- from lionagi.core.generic import BaseNode, DataLogger, DLog
8
- from lionagi.core.messages.schema import (
9
- BranchColumns,
10
- System,
11
- Response,
12
- Instruction,
13
- BaseMessage,
14
- )
15
- from lionagi.core.branch.util import MessageUtil
16
-
17
-
18
- class BaseBranch(BaseNode, ABC):
19
- """
20
- Base class for managing branches of conversation, incorporating messages
21
- and logging functionality.
22
-
23
- Attributes:
24
- messages (dataframe.ln_DataFrame): Holds the messages in the branch.
25
- datalogger (DataLogger): Logs data related to the branch's operation.
26
- persist_path (str | Path): Filesystem path for data persistence.
27
- """
28
-
29
- _columns: list[str] = BranchColumns.COLUMNS.value
30
-
31
- def __init__(
32
- self,
33
- messages: dataframe.ln_DataFrame | None = None,
34
- datalogger: DataLogger | None = None,
35
- persist_path: str | Path | None = None,
36
- name=None,
37
- **kwargs,
38
- ) -> None:
39
- super().__init__(**kwargs)
40
- if isinstance(messages, dataframe.ln_DataFrame):
41
- if MessageUtil.validate_messages(messages):
42
- self.messages = messages
43
- else:
44
- raise ValueError("Invalid messages format")
45
- else:
46
- self.messages = dataframe.ln_DataFrame(columns=self._columns)
47
-
48
- self.datalogger = datalogger or DataLogger(persist_path=persist_path)
49
- self.name = name
50
-
51
- def add_message(
52
- self,
53
- system: dict | list | System | None = None,
54
- instruction: dict | list | Instruction | None = None,
55
- context: str | dict[str, Any] | None = None,
56
- response: dict | list | BaseMessage | None = None,
57
- output_fields=None,
58
- recipient=None,
59
- **kwargs,
60
- ) -> None:
61
- """
62
- Adds a message to the branch.
63
-
64
- Args:
65
- system: Information for creating a System message.
66
- instruction: Information for creating an Instruction message.
67
- context: Context information for the message.
68
- response: Response data for creating a message.
69
- **kwargs: Additional keyword arguments for message creation.
70
- """
71
- _msg = MessageUtil.create_message(
72
- system=system,
73
- instruction=instruction,
74
- context=context,
75
- response=response,
76
- output_fields=output_fields,
77
- recipient=recipient,
78
- **kwargs,
79
- )
80
-
81
- if isinstance(_msg, System):
82
- self.system_node = _msg
83
-
84
- # sourcery skip: merge-nested-ifs
85
- if isinstance(_msg, Instruction):
86
- if recipient is None and self.name is not None:
87
- _msg.recipient = self.name
88
-
89
- if isinstance(_msg, Response):
90
- if "action_response" in _msg.content.keys():
91
- if recipient is None and self.name is not None:
92
- _msg.recipient = self.name
93
- if recipient is not None and self.name is None:
94
- _msg.recipient = recipient
95
- if "response" in _msg.content.keys():
96
- if self.name is not None:
97
- _msg.sender = self.name
98
-
99
- setattr(_msg, "node_id", _msg.id_)
100
- _msg.content = _msg.msg_content
101
- self.messages.loc[len(self.messages)] = _msg.to_pd_series()
102
-
103
- def _to_chatcompletion_message(
104
- self, with_sender: bool = False
105
- ) -> list[dict[str, Any]]:
106
- """
107
- Converts messages to a list of dictionaries formatted for chat completion,
108
- optionally including sender information.
109
-
110
- Args:
111
- with_sender: Flag to include sender information in the output.
112
-
113
- Returns:
114
- A list of message dictionaries, each with 'role' and 'content' keys,
115
- and optionally prefixed by 'Sender' if with_sender is True.
116
- """
117
-
118
- message = []
119
-
120
- for _, row in self.messages.iterrows():
121
- content_ = row["content"]
122
- if content_.startswith("Sender"):
123
- content_ = content_.split(":", 1)[1]
124
-
125
- # if isinstance(content_, str):
126
- # try:
127
- # content_ = json.dumps(to_dict(content_))
128
- # except Exception as e:
129
- # raise ValueError(
130
- # f"Error in serializing, {row['node_id']} {content_}: {e}"
131
- # )
132
-
133
- out = {"role": row["role"], "content": content_}
134
- if with_sender:
135
- out["content"] = f"Sender {row['sender']}: {content_}"
136
-
137
- message.append(out)
138
- return message
139
-
140
- @property
141
- def chat_messages(self) -> list[dict[str, Any]]:
142
- """
143
- Retrieves all chat messages without sender information.
144
-
145
- Returns:
146
- A list of dictionaries representing chat messages.
147
- """
148
-
149
- return self._to_chatcompletion_message()
150
-
151
- @property
152
- def chat_messages_with_sender(self) -> list[dict[str, Any]]:
153
- """
154
- Retrieves all chat messages, including sender information.
155
-
156
- Returns:
157
- A list of dictionaries representing chat messages, each prefixed with its sender.
158
- """
159
-
160
- return self._to_chatcompletion_message(with_sender=True)
161
-
162
- @property
163
- def last_message(self) -> dataframe.ln_DataFrame:
164
- """
165
- Retrieves the last message from the branch as a pandas Series.
166
-
167
- Returns:
168
- A pandas Series representing the last message in the branch.
169
- """
170
-
171
- return MessageUtil.get_message_rows(self.messages, n=1, from_="last")
172
-
173
- @property
174
- def last_message_content(self) -> dict[str, Any]:
175
- """
176
- Extracts the content of the last message in the branch.
177
-
178
- Returns:
179
- A dictionary representing the content of the last message.
180
- """
181
-
182
- return convert.to_dict(self.messages.content.iloc[-1])
183
-
184
- @property
185
- def first_system(self) -> dataframe.ln_DataFrame:
186
- """
187
- Retrieves the first message marked with the 'system' role.
188
-
189
- Returns:
190
- A pandas Series representing the first 'system' message in the branch.
191
- """
192
-
193
- return MessageUtil.get_message_rows(
194
- self.messages, role="system", n=1, from_="front"
195
- )
196
-
197
- @property
198
- def last_response(self) -> dataframe.ln_DataFrame:
199
- """
200
- Retrieves the last message marked with the 'assistant' role.
201
-
202
- Returns:
203
- A pandas Series representing the last 'assistant' (response) message in the branch.
204
- """
205
-
206
- return MessageUtil.get_message_rows(
207
- self.messages, role="assistant", n=1, from_="last"
208
- )
209
-
210
- @property
211
- def last_response_content(self) -> dict[str, Any]:
212
- """
213
- Extracts the content of the last 'assistant' (response) message.
214
-
215
- Returns:
216
- A dictionary representing the content of the last 'assistant' message.
217
- """
218
-
219
- return convert.to_dict(self.last_response.content.iloc[-1])
220
-
221
- @property
222
- def action_request(self) -> dataframe.ln_DataFrame:
223
- """
224
- Filters and retrieves all messages sent by 'action_request'.
225
-
226
- Returns:
227
- A pandas DataFrame containing all 'action_request' messages.
228
- """
229
-
230
- return convert.to_df(self.messages[self.messages.sender == "action_request"])
231
-
232
- @property
233
- def action_response(self) -> dataframe.ln_DataFrame:
234
- """
235
- Filters and retrieves all messages sent by 'action_response'.
236
-
237
- Returns:
238
- A pandas DataFrame containing all 'action_response' messages.
239
- """
240
-
241
- return convert.to_df(self.messages[self.messages.sender == "action_response"])
242
-
243
- @property
244
- def responses(self) -> dataframe.ln_DataFrame:
245
- """
246
- Retrieves all messages marked with the 'assistant' role.
247
-
248
- Returns:
249
- A pandas DataFrame containing all messages with an 'assistant' role.
250
- """
251
-
252
- return convert.to_df(self.messages[self.messages.role == "assistant"])
253
-
254
- @property
255
- def assistant_responses(self) -> dataframe.ln_DataFrame:
256
- """
257
- Filters 'assistant' role messages excluding 'action_request' and 'action_response'.
258
-
259
- Returns:
260
- A pandas DataFrame of 'assistant' messages excluding action requests/responses.
261
- """
262
-
263
- a_responses = self.responses[self.responses.sender != "action_response"]
264
- a_responses = a_responses[a_responses.sender != "action_request"]
265
- return convert.to_df(a_responses)
266
-
267
- @property
268
- def last_assistant_response(self):
269
- return self.assistant_responses.iloc[-1]
270
-
271
- @property
272
- def info(self) -> dict[str, Any]:
273
- """
274
- Summarizes branch information, including message counts by role.
275
-
276
- Returns:
277
- A dictionary containing counts of messages categorized by their role.
278
- """
279
-
280
- return self._info()
281
-
282
- @property
283
- def sender_info(self) -> dict[str, int]:
284
- """
285
- Provides a summary of message counts categorized by sender.
286
-
287
- Returns:
288
- A dictionary with senders as keys and counts of their messages as values.
289
- """
290
-
291
- return self._info(use_sender=True)
292
-
293
- @property
294
- def describe(self) -> dict[str, Any]:
295
- """
296
- Provides a detailed description of the branch, including a summary of messages.
297
-
298
- Returns:
299
- A dictionary with a summary of total messages, a breakdown by role, and
300
- a preview of the first five messages.
301
- """
302
-
303
- return {
304
- "total_messages": len(self.messages),
305
- "summary_by_role": self._info(),
306
- "messages": [msg.to_dict() for _, msg in self.messages.iterrows()][
307
- : len(self.messages) - 1 if len(self.messages) < 5 else 5
308
- ],
309
- }
310
-
311
- @classmethod
312
- def _from_csv(cls, filename: str, read_kwargs=None, **kwargs) -> "BaseBranch":
313
- read_kwargs = {} if read_kwargs is None else read_kwargs
314
- messages = dataframe.read_csv(filename, **read_kwargs)
315
- return cls(messages=messages, **kwargs)
316
-
317
- @classmethod
318
- def from_csv(cls, **kwargs) -> "BaseBranch":
319
-
320
- return cls._from_csv(**kwargs)
321
-
322
- @classmethod
323
- def from_json_string(cls, **kwargs) -> "BaseBranch":
324
-
325
- return cls._from_json(**kwargs)
326
-
327
- @classmethod
328
- def _from_json(cls, filename: str, read_kwargs=None, **kwargs) -> "BaseBranch":
329
- read_kwargs = {} if read_kwargs is None else read_kwargs
330
- messages = dataframe.read_json(filename, **read_kwargs)
331
- return cls(messages=messages, **kwargs)
332
-
333
- def to_csv_file(
334
- self,
335
- filename: str | Path = "messages.csv",
336
- dir_exist_ok: bool = True,
337
- timestamp: bool = True,
338
- time_prefix: bool = False,
339
- verbose: bool = True,
340
- clear: bool = True,
341
- **kwargs,
342
- ) -> None:
343
- """
344
- Exports the branch messages to a CSV file.
345
-
346
- Args:
347
- filepath: Destination path for the CSV file. Defaults to 'messages.csv'.
348
- dir_exist_ok: If False, an error is raised if the directory exists. Defaults to True.
349
- timestamp: If True, appends a timestamp to the filename. Defaults to True.
350
- time_prefix: If True, prefixes the filename with a timestamp. Defaults to False.
351
- verbose: If True, prints a message upon successful export. Defaults to True.
352
- clear: If True, clears the messages after exporting. Defaults to True.
353
- **kwargs: Additional keyword arguments for pandas.DataFrame.to_csv().
354
- """
355
-
356
- if not filename.endswith(".csv"):
357
- filename += ".csv"
358
-
359
- filename = SysUtil.create_path(
360
- self.datalogger.persist_path,
361
- filename,
362
- timestamp=timestamp,
363
- dir_exist_ok=dir_exist_ok,
364
- time_prefix=time_prefix,
365
- )
366
-
367
- try:
368
- self.messages.to_csv(filename, **kwargs)
369
- if verbose:
370
- print(f"{len(self.messages)} messages saved to {filename}")
371
- if clear:
372
- self.clear_messages()
373
- except Exception as e:
374
- raise ValueError(f"Error in saving to csv: {e}") from e
375
-
376
- def to_json_file(
377
- self,
378
- filename: str | Path = "messages.json",
379
- dir_exist_ok: bool = True,
380
- timestamp: bool = True,
381
- time_prefix: bool = False,
382
- verbose: bool = True,
383
- clear: bool = True,
384
- **kwargs,
385
- ) -> None:
386
- """
387
- Exports the branch messages to a JSON file.
388
-
389
- Args:
390
- filename: Destination path for the JSON file. Defaults to 'messages.json'.
391
- dir_exist_ok: If False, an error is raised if the dirctory exists. Defaults to True.
392
- timestamp: If True, appends a timestamp to the filename. Defaults to True.
393
- time_prefix: If True, prefixes the filename with a timestamp. Defaults to False.
394
- verbose: If True, prints a message upon successful export. Defaults to True.
395
- clear: If True, clears the messages after exporting. Defaults to True.
396
- **kwargs: Additional keyword arguments for pandas.DataFrame.to_json().
397
- """
398
-
399
- if not filename.endswith(".json"):
400
- filename += ".json"
401
-
402
- filename = SysUtil.create_path(
403
- self.datalogger.persist_path,
404
- filename,
405
- timestamp=timestamp,
406
- dir_exist_ok=dir_exist_ok,
407
- time_prefix=time_prefix,
408
- )
409
-
410
- try:
411
- self.messages.to_json(
412
- filename, orient="records", lines=True, date_format="iso", **kwargs
413
- )
414
- if verbose:
415
- print(f"{len(self.messages)} messages saved to {filename}")
416
- if clear:
417
- self.clear_messages()
418
- except Exception as e:
419
- raise ValueError(f"Error in saving to json: {e}") from e
420
-
421
- def log_to_csv(
422
- self,
423
- filename: str | Path = "log.csv",
424
- dir_exist_ok: bool = True,
425
- timestamp: bool = True,
426
- time_prefix: bool = False,
427
- verbose: bool = True,
428
- clear: bool = True,
429
- flatten_=True,
430
- sep="[^_^]",
431
- **kwargs,
432
- ) -> None:
433
- """
434
- Exports the data logger contents to a CSV file.
435
-
436
- Args:
437
- filename: Destination path for the CSV file. Defaults to 'log.csv'.
438
- dir_exist_ok: If False, an error is raised if the directory exists. Defaults to True.
439
- timestamp: If True, appends a timestamp to the filename. Defaults to True.
440
- time_prefix: If True, prefixes the filename with a timestamp. Defaults to False.
441
- verbose: If True, prints a message upon successful export. Defaults to True.
442
- clear: If True, clears the logger after exporting. Defaults to True.
443
- **kwargs: Additional keyword arguments for pandas.DataFrame.to_csv().
444
- """
445
- self.datalogger.to_csv_file(
446
- filename=filename,
447
- dir_exist_ok=dir_exist_ok,
448
- timestamp=timestamp,
449
- time_prefix=time_prefix,
450
- verbose=verbose,
451
- clear=clear,
452
- flatten_=flatten_,
453
- sep=sep,
454
- **kwargs,
455
- )
456
-
457
- def log_to_json(
458
- self,
459
- filename: str | Path = "log.json",
460
- dir_exist_ok: bool = True,
461
- timestamp: bool = True,
462
- time_prefix: bool = False,
463
- verbose: bool = True,
464
- clear: bool = True,
465
- flatten_=True,
466
- sep="[^_^]",
467
- **kwargs,
468
- ) -> None:
469
- """
470
- Exports the data logger contents to a JSON file.
471
-
472
- Args:
473
- filename: Destination path for the JSON file. Defaults to 'log.json'.
474
- dir_exist_ok: If False, an error is raised if the directory exists. Defaults to True.
475
- timestamp: If True, appends a timestamp to the filename. Defaults to True.
476
- time_prefix: If True, prefixes the filename with a timestamp. Defaults to False.
477
- verbose: If True, prints a message upon successful export. Defaults to True.
478
- clear: If True, clears the logger after exporting. Defaults to True.
479
- **kwargs: Additional keyword arguments for pandas.DataFrame.to_json().
480
- """
481
-
482
- self.datalogger.to_json_file(
483
- filename=filename,
484
- dir_exist_ok=dir_exist_ok,
485
- timestamp=timestamp,
486
- time_prefix=time_prefix,
487
- verbose=verbose,
488
- clear=clear,
489
- flatten_=flatten_,
490
- sep=sep,
491
- **kwargs,
492
- )
493
-
494
- def load_log(self, filename, flattened=True, sep="[^_^]", verbose=True, **kwargs):
495
- df = ""
496
- try:
497
- if filename.endswith(".csv"):
498
- df = dataframe.read_csv(filename, **kwargs)
499
-
500
- elif filename.endswith(".json"):
501
- df = dataframe.read_json(filename, **kwargs)
502
-
503
- for _, row in df.iterrows():
504
- self.datalogger.log.append(
505
- DLog.deserialize(
506
- input_str=row.input_data,
507
- output_str=row.output_data,
508
- unflatten_=flattened,
509
- sep=sep,
510
- )
511
- )
512
-
513
- if verbose:
514
- print(f"Loaded {len(df)} logs from {filename}")
515
- except Exception as e:
516
- raise ValueError(f"Error in loading log: {e}") from e
517
-
518
- def remove_message(self, node_id: str) -> None:
519
- """
520
- Removes a message from the branch based on its node ID.
521
-
522
- Args:
523
- node_id: The unique identifier of the message to be removed.
524
- """
525
- MessageUtil.remove_message(self.messages, node_id)
526
-
527
- def update_message(self, node_id: str, column: str, value: Any) -> bool:
528
- """
529
- Updates a specific column of a message identified by node_id with a new value.
530
-
531
- Args:
532
- value: The new value to update the message with.
533
- node_id: The unique identifier of the message to update.
534
- column: The column of the message to update.
535
- """
536
-
537
- index = self.messages[self.messages["node_id"] == node_id].index[0]
538
-
539
- return dataframe.update_row(
540
- self.messages, row=index, column=column, value=value
541
- )
542
-
543
- def change_first_system_message(
544
- self, system: str | dict[str, Any] | System, sender: str | None = None
545
- ) -> None:
546
- """
547
- Updates the first system message with new content and/or sender.
548
-
549
- Args:
550
- system: The new system message content or a System object.
551
- sender: The identifier of the sender for the system message.
552
- """
553
-
554
- if len(self.messages[self.messages["role"] == "system"]) == 0:
555
- raise ValueError("There is no system message in the messages.")
556
-
557
- if not isinstance(system, (str, dict, System)):
558
- raise ValueError("Input cannot be converted into a system message.")
559
-
560
- if isinstance(system, (str, dict)):
561
- system = System(system, sender=sender)
562
-
563
- if isinstance(system, System):
564
- system.timestamp = SysUtil.get_timestamp()
565
- sys_index = self.messages[self.messages.role == "system"].index
566
- self.messages.loc[sys_index[0]] = system.to_pd_series()
567
-
568
- def rollback(self, steps: int) -> None:
569
- """
570
- Removes the last 'n' messages from the branch.
571
-
572
- Args:
573
- steps: The number of messages to remove from the end.
574
- """
575
-
576
- self.messages = dataframe.remove_last_n_rows(self.messages, steps)
577
-
578
- def clear_messages(self) -> None:
579
- """
580
- Clears all messages from the branch.
581
- """
582
- self.messages = dataframe.ln_DataFrame(columns=self._columns)
583
-
584
- def replace_keyword(
585
- self,
586
- keyword: str,
587
- replacement: str,
588
- column: str = "content",
589
- case_sensitive: bool = False,
590
- ) -> None:
591
-
592
- dataframe.replace_keyword(
593
- self.messages,
594
- keyword,
595
- replacement,
596
- column=column,
597
- case_sensitive=case_sensitive,
598
- )
599
-
600
- def search_keywords(
601
- self,
602
- keywords: str | list[str],
603
- case_sensitive: bool = False,
604
- reset_index: bool = False,
605
- dropna: bool = False,
606
- ) -> dataframe.ln_DataFrame:
607
- return dataframe.search_keywords(
608
- self.messages,
609
- keywords,
610
- case_sensitive=case_sensitive,
611
- reset_index=reset_index,
612
- dropna=dropna,
613
- )
614
-
615
- def extend(self, messages: dataframe.ln_DataFrame, **kwargs) -> None:
616
-
617
- self.messages = MessageUtil.extend(self.messages, messages, **kwargs)
618
-
619
- def filter_by(
620
- self,
621
- role: str | None = None,
622
- sender: str | None = None,
623
- start_time=None,
624
- end_time=None,
625
- content_keywords: str | list[str] | None = None,
626
- case_sensitive: bool = False,
627
- ) -> dataframe.ln_DataFrame:
628
-
629
- return MessageUtil.filter_messages_by(
630
- self.messages,
631
- role=role,
632
- sender=sender,
633
- start_time=start_time,
634
- end_time=end_time,
635
- content_keywords=content_keywords,
636
- case_sensitive=case_sensitive,
637
- )
638
-
639
- def _info(self, use_sender: bool = False) -> dict[str, int]:
640
- """
641
- Helper method to generate summaries of messages either by role or sender.
642
-
643
- Args:
644
- use_sender: If True, summary is categorized by sender. Otherwise, by role.
645
-
646
- Returns:
647
- A dictionary summarizing the count of messages either by role or sender.
648
- """
649
-
650
- messages = self.messages["sender"] if use_sender else self.messages["role"]
651
- result = messages.value_counts().to_dict()
652
- result["total"] = len(self.messages)
653
- return result