lionagi 0.0.201__py3-none-any.whl → 0.0.204__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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