lionagi 0.0.201__py3-none-any.whl → 0.0.204__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 (53) hide show
  1. lionagi/_services/anthropic.py +79 -1
  2. lionagi/_services/base_service.py +1 -1
  3. lionagi/_services/services.py +61 -25
  4. lionagi/_services/transformers.py +46 -0
  5. lionagi/agents/__init__.py +0 -0
  6. lionagi/configs/oai_configs.py +1 -1
  7. lionagi/configs/openrouter_configs.py +1 -1
  8. lionagi/core/__init__.py +3 -7
  9. lionagi/core/branch/__init__.py +0 -0
  10. lionagi/core/branch/branch.py +589 -0
  11. lionagi/core/branch/branch_manager.py +139 -0
  12. lionagi/core/branch/cluster.py +1 -0
  13. lionagi/core/branch/conversation.py +484 -0
  14. lionagi/core/core_util.py +59 -0
  15. lionagi/core/flow/__init__.py +0 -0
  16. lionagi/core/flow/flow.py +19 -0
  17. lionagi/core/instruction_set/__init__.py +0 -0
  18. lionagi/core/instruction_set/instruction_set.py +343 -0
  19. lionagi/core/messages/__init__.py +0 -0
  20. lionagi/core/messages/messages.py +176 -0
  21. lionagi/core/sessions/__init__.py +0 -0
  22. lionagi/core/sessions/session.py +428 -0
  23. lionagi/models/__init__.py +0 -0
  24. lionagi/models/base_model.py +0 -0
  25. lionagi/models/imodel.py +53 -0
  26. lionagi/schema/data_logger.py +75 -155
  27. lionagi/tests/test_utils/test_call_util.py +658 -657
  28. lionagi/tools/tool_manager.py +121 -188
  29. lionagi/utils/__init__.py +5 -10
  30. lionagi/utils/call_util.py +667 -585
  31. lionagi/utils/io_util.py +3 -0
  32. lionagi/utils/nested_util.py +17 -211
  33. lionagi/utils/pd_util.py +57 -0
  34. lionagi/utils/sys_util.py +220 -184
  35. lionagi/utils/url_util.py +55 -0
  36. lionagi/version.py +1 -1
  37. {lionagi-0.0.201.dist-info → lionagi-0.0.204.dist-info}/METADATA +12 -8
  38. {lionagi-0.0.201.dist-info → lionagi-0.0.204.dist-info}/RECORD +47 -32
  39. lionagi/core/branch.py +0 -193
  40. lionagi/core/conversation.py +0 -341
  41. lionagi/core/flow.py +0 -8
  42. lionagi/core/instruction_set.py +0 -150
  43. lionagi/core/messages.py +0 -243
  44. lionagi/core/sessions.py +0 -474
  45. /lionagi/{tools → agents}/planner.py +0 -0
  46. /lionagi/{tools → agents}/prompter.py +0 -0
  47. /lionagi/{tools → agents}/scorer.py +0 -0
  48. /lionagi/{tools → agents}/summarizer.py +0 -0
  49. /lionagi/{tools → agents}/validator.py +0 -0
  50. /lionagi/core/{flow_util.py → flow/flow_util.py} +0 -0
  51. {lionagi-0.0.201.dist-info → lionagi-0.0.204.dist-info}/LICENSE +0 -0
  52. {lionagi-0.0.201.dist-info → lionagi-0.0.204.dist-info}/WHEEL +0 -0
  53. {lionagi-0.0.201.dist-info → lionagi-0.0.204.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,19 @@
1
+ """async def critic_workflow():
2
+
3
+ @ cd.max_concurrency(limit=3)
4
+ async def run_critic_stage(critic_idx=None, step_num=None, outs=None, max_num_tool_uses=5):
5
+
6
+ async def inner_func_():
7
+ critic_ = f"critic{critic_idx}"
8
+ name_ = li.nget(critics, [critic_, "name"])
9
+ tool = li.nget(critics, [critic_, "tool"])
10
+ instruction_ = li.nget(critics, [critic_, f"step{step_num}"])
11
+
12
+
13
+
14
+ out = await researcher.auto_followup(branch=name_, instruction=instruction_, tools=tool, num=max_num_tool_uses)
15
+ add_name_to_messages(name_)
16
+ last_response = last_response_row(df=researcher.branches[name_].messages, name_=name_)
17
+ outs.append(last_response)
18
+
19
+ return out"""
File without changes
@@ -0,0 +1,343 @@
1
+ from typing import List, Union
2
+ from ...schema import Tool
3
+ from ...structures import Relationship, Structure
4
+ from ..messages.messages import Instruction
5
+
6
+
7
+ class InstructionSet(Structure):
8
+ """
9
+ Represents a set of instructions and their relationships to tools.
10
+
11
+ Attributes:
12
+ last_instruct (Optional[str]): ID of the last instruction added.
13
+ first_instruct (Optional[str]): ID of the first instruction added.
14
+ instruct_len (int): The total number of instructions.
15
+
16
+ Example usage:
17
+ >>> instruction_set = InstructionSet()
18
+ >>> instruction = Instruction(...)
19
+ >>> tool = Tool(...)
20
+ >>> instruction_set.add_instruction(instruction)
21
+ >>> instruction_set.add_instruction(instruction, tools=tool)
22
+ >>> instruction_set.pop_instruction()
23
+ >>> instruction_node = instruction_set.get_instruction_by_id('node_id')
24
+ >>> next_instruction = instruction_set.get_next_instruction(instruction_node)
25
+ >>> tools = instruction_set.get_associated_tools(instruction_node)
26
+ """
27
+ last_instruct: str = None
28
+ first_instruct: str = None
29
+ instruct_len: int = 0
30
+
31
+ def add_instruction(self, instruction: Instruction, tools: Union[Tool, List[Tool]] = None):
32
+ """
33
+ Add an instruction to the instruction set.
34
+
35
+ Args:
36
+ instruction (Instruction): The instruction to add.
37
+ tools (Union[Tool, List[Tool]], optional): The tool or list of tools related to the instruction.
38
+
39
+ Example usage:
40
+ >>> instruction_set = InstructionSet()
41
+ >>> instruction = Instruction(...)
42
+ >>> tool = Tool(...)
43
+ >>> instruction_set.push_instruction(instruction)
44
+ >>> instruction_set.push_instruction(instruction, tools=tool)
45
+ """
46
+
47
+ if self.graph.is_empty():
48
+ self.graph.add_node(instruction)
49
+ self.last_instruct = instruction.id_
50
+ self.first_instruct = instruction.id_
51
+ else:
52
+ relationship = Relationship(source_node_id=self.last_instruct, target_node_id=instruction.id_, label='instruction')
53
+ self.graph.add_node(instruction)
54
+ self.graph.add_relationship(relationship)
55
+ self.last_instruct = instruction.id_
56
+ self.instruct_len += 1
57
+
58
+ if tools:
59
+ if isinstance(tools, Tool):
60
+ tools = [tools]
61
+ for tool in tools:
62
+ relationship = Relationship(source_node_id=tool.id_, target_node_id=self.last_instruct, label='tool')
63
+ self.graph.add_node(tool)
64
+ self.graph.add_relationship(relationship)
65
+
66
+ def pop_instruction(self):
67
+ """
68
+ Remove the last instruction from the instruction set.
69
+
70
+ Example usage:
71
+ >>> instruction_set = InstructionSet()
72
+ >>> instruction_set.pop_instruction()
73
+ """
74
+ if self.graph.is_empty():
75
+ return
76
+ elif self.instruct_len == 1:
77
+ self.graph.clear()
78
+ self.last_instruct = None
79
+ self.first_instruct = None
80
+ self.instruct_len -= 1
81
+ else:
82
+ relationships = self.get_node_relationships(self.graph.nodes[self.last_instruct], out_edge=False)
83
+ prev_instruct = None
84
+ for r in relationships:
85
+ if r.label != 'instruction':
86
+ self.graph.remove_node(self.graph.nodes[r.source_node_id])
87
+ else:
88
+ prev_instruct = r.source_node_id
89
+
90
+ self.graph.remove_node(self.graph.nodes[self.last_instruct])
91
+ self.last_instruct = prev_instruct
92
+ self.instruct_len -= 1
93
+
94
+ def get_instruction_by_id(self, node_id):
95
+ """
96
+ Retrieve an instruction by its ID.
97
+
98
+ Args:
99
+ node_id (str): The ID of the instruction node.
100
+
101
+ Returns:
102
+ Instruction: The instruction node associated with the given ID.
103
+
104
+ Example usage:
105
+ >>> instruction_set = InstructionSet()
106
+ >>> instruction_node = instruction_set.get_instruction_by_id('node_id')
107
+ """
108
+ return self.graph.nodes[node_id]
109
+
110
+ def get_next_instruction(self, instruct_node: Instruction):
111
+ """
112
+ Retrieve the next instruction following the given instruction node.
113
+
114
+ Args:
115
+ instruct_node (Instruction): The current instruction node.
116
+
117
+ Returns:
118
+ Optional[Instruction]: The next instruction node in the sequence, if it exists.
119
+
120
+ Example usage:
121
+ >>> instruction_set = InstructionSet()
122
+ >>> current_node = Instruction(...)
123
+ >>> next_node = instruction_set.get_next_instruction(current_node)
124
+ """
125
+ relationship = self.get_node_relationships(instruct_node)
126
+ if relationship:
127
+ return self.graph.nodes[relationship[0].target_node_id]
128
+
129
+ def get_tools(self, instruct_node: Instruction):
130
+ """
131
+ Retrieve the tools associated with a given instruction node.
132
+
133
+ Args:
134
+ instruct_node (Instruction): The instruction node to retrieve tools for.
135
+
136
+ Returns:
137
+ List[Tool]: The tools associated with the instruction node.
138
+
139
+ Example usage:
140
+ >>> instruction_set = InstructionSet()
141
+ >>> instruction_node = Instruction(...)
142
+ >>> tools = instruction_set.get_tools(instruction_node)
143
+ """
144
+ relationships = self.get_node_relationships(instruct_node, out_edge=False, labels=['tool'])
145
+ tools = []
146
+ for r in relationships:
147
+ tool = self.graph.nodes[r.source_node_id]
148
+ tools.append(tool)
149
+ return tools
150
+
151
+ # # new methods
152
+ # def insert_instruction(self, index: int, instruction: Instruction, tools: Union[Tool, List[Tool]] = None):
153
+ # """
154
+ # Insert an instruction at a specified index in the instruction set.
155
+ #
156
+ # Args:
157
+ # index (int): The index to insert the instruction at.
158
+ # instruction (Instruction): The instruction to insert.
159
+ # tools (Union[Tool, List[Tool]], optional): The tool or list of tools related to the instruction.
160
+ # """
161
+ # if index < 0 or index > self.instruct_len:
162
+ # raise IndexError("Index out of bounds.")
163
+ #
164
+ # if index == self.instruct_len:
165
+ # self.add_instruction(instruction, tools)
166
+ # return
167
+ #
168
+ # # Adjust relationships and insert the new instruction
169
+ # current_node = self.first_instruct
170
+ # for _ in range(index):
171
+ # next_node = self.get_next_instruction(self.graph.nodes[current_node])
172
+ # current_node = next_node.id_
173
+ #
174
+ # prev_node = self.graph.get_node_relationships(current_node, out_edge=False)
175
+ # prev_node_id = prev_node[0].source_node_id if prev_node else None
176
+ #
177
+ # if prev_node_id:
178
+ # self.graph.remove_relationship_between(prev_node_id, current_node)
179
+ #
180
+ # new_rel = Relationship(prev_node_id, instruction.id_, 'instruction')
181
+ # self.graph.add_node(instruction)
182
+ # self.graph.add_relationship(new_rel)
183
+ # self.graph.add_relationship(Relationship(instruction.id_, current_node, 'instruction'))
184
+ #
185
+ # self.instruct_len += 1
186
+ # self._add_tools_to_instruction(instruction, tools)
187
+ #
188
+ # def replace_instruction(self, instruction_id: str, new_instruction: Instruction):
189
+ # """
190
+ # Replace an existing instruction with a new instruction.
191
+ #
192
+ # Args:
193
+ # instruction_id (str): The ID of the instruction to replace.
194
+ # new_instruction (Instruction): The new instruction to replace with.
195
+ # """
196
+ # if not self.graph.node_exist(instruction_id):
197
+ # return False
198
+ #
199
+ # prev_rel = self.graph.get_node_relationships(instruction_id, out_edge=False)
200
+ # next_rel = self.graph.get_node_relationships(instruction_id)
201
+ #
202
+ # if prev_rel:
203
+ # self.graph.remove_relationship(prev_rel[0])
204
+ # self.graph.add_relationship(Relationship(prev_rel[0].source_node_id, new_instruction.id_, 'instruction'))
205
+ #
206
+ # if next_rel:
207
+ # self.graph.remove_relationship(next_rel[0])
208
+ # self.graph.add_relationship(Relationship(new_instruction.id_, next_rel[0].target_node_id, 'instruction'))
209
+ #
210
+ # self.graph.replace_node(instruction_id, new_instruction)
211
+ # return True
212
+ #
213
+ # def move_instruction(self, instruction_id: str, new_index: int):
214
+ # """
215
+ # Move an instruction to a new index within the instruction set.
216
+ #
217
+ # Args:
218
+ # instruction_id (str): The ID of the instruction to move.
219
+ # new_index (int): The new index to move the instruction to.
220
+ # """
221
+ # if not self.graph.node_exist(instruction_id) or new_index < 0 or new_index >= self.instruct_len:
222
+ # return False
223
+ #
224
+ # # Remove the instruction from its current position
225
+ # removed_instruction = self.graph.remove_node(instruction_id)
226
+ # if removed_instruction:
227
+ # # Reinsert at the new position
228
+ # self.insert_instruction(new_index, removed_instruction)
229
+ # return True
230
+ # return False
231
+ #
232
+ # def clear_instructions(self):
233
+ # """
234
+ # Clear all instructions from the instruction set.
235
+ # """
236
+ # self.graph.clear()
237
+ # self.last_instruct = None
238
+ # self.first_instruct = None
239
+ # self.instruct_len = 0
240
+ #
241
+ # def _add_tools_to_instruction(self, instruction: Instruction, tools: Union[Tool, List[Tool]]):
242
+ # """
243
+ # Helper method to add tools to an instruction.
244
+ #
245
+ # Args:
246
+ # instruction (Instruction): The instruction to associate tools with.
247
+ # tools (Union[Tool, List[Tool]]): The tool or list of tools to associate.
248
+ # """
249
+ # if tools:
250
+ # if isinstance(tools, Tool):
251
+ # tools = [tools]
252
+ # for tool in tools:
253
+ # relationship = Relationship(source_node_id=tool.id_, target_node_id=instruction.id_, label='tool')
254
+ # self.graph.add_node(tool)
255
+ # self.graph.add_relationship(relationship)
256
+ #
257
+ #
258
+ # def get_previous_instruction(self, instruct_node: Instruction):
259
+ # """
260
+ # Retrieve the previous instruction before the given instruction node.
261
+ #
262
+ # Args:
263
+ # instruct_node (Instruction): The current instruction node.
264
+ #
265
+ # Returns:
266
+ # Optional[Instruction]: The previous instruction node, if it exists.
267
+ #
268
+ # Example usage:
269
+ # >>> instruction_set = InstructionSet()
270
+ # >>> current_node = Instruction(...)
271
+ # >>> prev_node = instruction_set.get_previous_instruction(current_node)
272
+ # """
273
+ # relationship = self.get_node_relationships(instruct_node, out_edge=False)
274
+ # if relationship:
275
+ # return self.graph.nodes[relationship[0].source_node_id]
276
+ #
277
+ # def get_all_instructions(self):
278
+ # """
279
+ # Retrieve all instructions in the set.
280
+ #
281
+ # Returns:
282
+ # List[Instruction]: List of all instructions in the set.
283
+ #
284
+ # Example usage:
285
+ # >>> instruction_set = InstructionSet()
286
+ # >>> all_instructions = instruction_set.get_all_instructions()
287
+ # """
288
+ # instructions = []
289
+ # current_node_id = self.first_instruct
290
+ # while current_node_id:
291
+ # current_node = self.graph.nodes[current_node_id]
292
+ # instructions.append(current_node)
293
+ # next_rel = self.get_node_relationships(current_node)
294
+ # current_node_id = next_rel[0].target_node_id if next_rel else None
295
+ # return instructions
296
+ #
297
+ # def find_instructions_by_label(self, label: str):
298
+ # """
299
+ # Find all instructions that have a specific label.
300
+ #
301
+ # Args:
302
+ # label (str): The label to search for in instructions.
303
+ #
304
+ # Returns:
305
+ # List[Instruction]: List of instructions that have the specified label.
306
+ #
307
+ # Example usage:
308
+ # >>> instruction_set = InstructionSet()
309
+ # >>> specific_instructions = instruction_set.find_instructions_by_label('label_name')
310
+ # """
311
+ # return [node for node in self.graph.nodes.values() if isinstance(node, Instruction) and node.label == label]
312
+
313
+ # def remove_instruction_by_id(self, instruction_id: str):
314
+ # """
315
+ # Remove an instruction from the set by its ID.
316
+ #
317
+ # Args:
318
+ # instruction_id (str): The ID of the instruction to remove.
319
+ #
320
+ # Returns:
321
+ # bool: True if the instruction was removed, False otherwise.
322
+ #
323
+ # Example usage:
324
+ # >>> instruction_set = InstructionSet()
325
+ # >>> instruction_set.remove_instruction_by_id('instruction_id')
326
+ # """
327
+ # if instruction_id not in self.graph.nodes:
328
+ # return False
329
+ # to_remove = self.graph.nodes[instruction_id]
330
+ # prev_rel = self.graph.get_node_relationships(instruction_id, out_edge=False)
331
+ # next_rel = self.graph.get_node_relationships(instruction_id)
332
+ #
333
+ # if prev_rel and next_rel:
334
+ # self.graph.add_relationship(Relationship(prev_rel[0].source_node_id, next_rel[0].target_node_id, 'instruction'))
335
+ #
336
+ # self.graph.remove_node(to_remove)
337
+ # self.instruct_len -= 1
338
+ # if instruction_id == self.first_instruct:
339
+ # self.first_instruct = next_rel[0].target_node_id if next_rel else None
340
+ # if instruction_id == self.last_instruct:
341
+ # self.last_instruct = prev_rel[0].source_node_id if prev_rel else None
342
+ #
343
+ # return True
File without changes
@@ -0,0 +1,176 @@
1
+ from typing import Any, Dict, Optional
2
+ from lionagi.schema.base_node import BaseNode
3
+
4
+ from lionagi.utils.sys_util import strip_lower
5
+ from lionagi.utils.nested_util import nget
6
+
7
+ import json
8
+
9
+
10
+ class Message(BaseNode):
11
+ """
12
+ Represents a message in a chatbot-like system, inheriting from BaseNode.
13
+
14
+ Attributes:
15
+ role (Optional[str]): The role of the entity sending the message, e.g., 'user', 'system'.
16
+ sender (Optional[str]): The identifier of the sender of the message.
17
+ content (Any): The actual content of the message.
18
+ """
19
+
20
+
21
+ role: Optional[str] = None
22
+ sender: Optional[str] = None
23
+
24
+ @property
25
+ def msg(self) -> Dict[str, Any]:
26
+ """
27
+ Constructs and returns a dictionary representation of the message.
28
+
29
+ Returns:
30
+ A dictionary representation of the message with 'role' and 'content' keys.
31
+ """
32
+ return self._to_message()
33
+
34
+ @property
35
+ def msg_content(self) -> Any:
36
+ """
37
+ Gets the 'content' field of the message.
38
+
39
+ Returns:
40
+ The 'content' part of the message.
41
+ """
42
+ return self.msg['content']
43
+
44
+ @property
45
+ def sender(self) -> str:
46
+ return self.sender
47
+
48
+ def _to_message(self):
49
+ """
50
+ Constructs and returns a dictionary representation of the message.
51
+
52
+ Returns:
53
+ dict: A dictionary representation of the message with 'role' and 'content' keys.
54
+ """
55
+ out = {
56
+ "role": self.role,
57
+ "content": json.dumps(self.content) if isinstance(self.content, dict) else self.content
58
+ }
59
+ return out
60
+
61
+ def __str__(self):
62
+ content_preview = (
63
+ (str(self.content)[:75] + '...') if self.content and len(self.content) > 75
64
+ else str(self.content)
65
+ )
66
+ return f"Message(role={self.role}, sender={self.sender}, content='{content_preview}')"
67
+
68
+ class Instruction(Message):
69
+ """
70
+ Represents an instruction message, a specialized subclass of Message.
71
+
72
+ This class is specifically used for creating messages that are instructions from the user,
73
+ including any associated context. It sets the message role to 'user'.
74
+ """
75
+
76
+ def __init__(self, instruction: Any, context=None, sender: Optional[str] = None):
77
+ super().__init__(
78
+ role="user", sender=sender or 'user', content={"instruction": instruction}
79
+ )
80
+ if context:
81
+ self.content.update({"context": context})
82
+
83
+ class System(Message):
84
+ """
85
+ Represents a system-related message, a specialized subclass of Message.
86
+
87
+ Designed for messages containing system information, this class sets the message role to 'system'.
88
+ """
89
+ def __init__(self, system: Any, sender: Optional[str] = None):
90
+ super().__init__(
91
+ role="system", sender=sender or 'system', content={"system_info": system}
92
+ )
93
+
94
+ class Response(Message):
95
+ """
96
+ Represents a response message, a specialized subclass of Message.
97
+
98
+ Used for various types of response messages including regular responses, action requests,
99
+ and action responses. It sets the message role to 'assistant'.
100
+
101
+ """
102
+
103
+ def __init__(self, response: Any, sender: Optional[str] = None) -> None:
104
+ content_key = ''
105
+ try:
106
+ response = response["message"]
107
+ if strip_lower(response['content']) == "none":
108
+ content_ = self._handle_action_request(response)
109
+ sender = sender or "action_request"
110
+ content_key = content_key or "action_list"
111
+
112
+ else:
113
+ try:
114
+ if 'tool_uses' in json.loads(response['content']):
115
+ content_ = json.loads(response['content'])['tool_uses']
116
+ content_key = content_key or "action_list"
117
+ sender = sender or "action_request"
118
+ elif 'response' in json.loads(response['content']):
119
+ sender = sender or "assistant"
120
+ content_key = content_key or "response"
121
+ content_ = json.loads(response['content'])['response']
122
+ elif 'action_list' in json.loads(response['content']):
123
+ sender = sender or "action_request"
124
+ content_key = content_key or "action_list"
125
+ content_ = json.loads(response['content'])['action_list']
126
+ else:
127
+ content_ = response['content']
128
+ content_key = content_key or "response"
129
+ sender = sender or "assistant"
130
+ except:
131
+ content_ = response['content']
132
+ content_key = content_key or "response"
133
+ sender = sender or "assistant"
134
+
135
+ except:
136
+ sender = sender or "action_response"
137
+ content_ = response
138
+ content_key = content_key or "action_response"
139
+
140
+ super().__init__(role="assistant", sender=sender, content={content_key: content_})
141
+
142
+ @staticmethod
143
+ def _handle_action_request(response):
144
+ """
145
+ Processes an action request response and extracts relevant information.
146
+
147
+ Args:
148
+ response (dict): The response dictionary containing tool calls and other information.
149
+
150
+ Returns:
151
+ list: A list of dictionaries, each representing a function call with action and arguments.
152
+
153
+ Raises:
154
+ ValueError: If the response does not conform to the expected format for action requests.
155
+ """
156
+ try:
157
+ tool_count = 0
158
+ func_list = []
159
+ while tool_count < len(response['tool_calls']):
160
+ _path = ['tool_calls', tool_count, 'type']
161
+
162
+ if nget(response, _path) == 'function':
163
+ _path1 = ['tool_calls', tool_count, 'function', 'name']
164
+ _path2 = ['tool_calls', tool_count, 'function', 'arguments']
165
+
166
+ func_content = {
167
+ "action": ("action_" + nget(response, _path1)),
168
+ "arguments": nget(response, _path2)
169
+ }
170
+ func_list.append(func_content)
171
+ tool_count += 1
172
+ return func_list
173
+ except:
174
+ raise ValueError(
175
+ "Response message must be one of regular response or function calling"
176
+ )
File without changes