lionagi 0.7.0__py3-none-any.whl → 0.7.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. lionagi/operations/ReAct/ReAct.py +2 -2
  2. lionagi/operations/communicate/communicate.py +0 -59
  3. lionagi/operations/interpret/interpret.py +1 -2
  4. lionagi/operations/operate/operate.py +10 -5
  5. lionagi/operations/parse/parse.py +0 -36
  6. lionagi/operations/plan/plan.py +3 -3
  7. lionagi/operatives/action/manager.py +105 -82
  8. lionagi/operatives/action/request_response_model.py +31 -0
  9. lionagi/operatives/action/tool.py +50 -20
  10. lionagi/protocols/_concepts.py +1 -1
  11. lionagi/protocols/adapters/adapter.py +25 -0
  12. lionagi/protocols/adapters/json_adapter.py +107 -27
  13. lionagi/protocols/adapters/pandas_/csv_adapter.py +55 -11
  14. lionagi/protocols/adapters/pandas_/excel_adapter.py +52 -10
  15. lionagi/protocols/adapters/pandas_/pd_dataframe_adapter.py +54 -4
  16. lionagi/protocols/adapters/pandas_/pd_series_adapter.py +40 -0
  17. lionagi/protocols/generic/element.py +1 -1
  18. lionagi/protocols/generic/pile.py +5 -8
  19. lionagi/protocols/graph/edge.py +1 -1
  20. lionagi/protocols/graph/graph.py +16 -8
  21. lionagi/protocols/graph/node.py +1 -1
  22. lionagi/protocols/mail/exchange.py +126 -15
  23. lionagi/protocols/mail/mail.py +33 -0
  24. lionagi/protocols/mail/mailbox.py +62 -0
  25. lionagi/protocols/mail/manager.py +97 -41
  26. lionagi/protocols/mail/package.py +57 -3
  27. lionagi/protocols/messages/action_request.py +77 -26
  28. lionagi/protocols/messages/action_response.py +55 -26
  29. lionagi/protocols/messages/assistant_response.py +50 -15
  30. lionagi/protocols/messages/base.py +36 -0
  31. lionagi/protocols/messages/instruction.py +175 -145
  32. lionagi/protocols/messages/manager.py +152 -56
  33. lionagi/protocols/messages/message.py +61 -25
  34. lionagi/protocols/messages/system.py +54 -19
  35. lionagi/service/imodel.py +24 -0
  36. lionagi/session/branch.py +40 -32
  37. lionagi/utils.py +1 -0
  38. lionagi/version.py +1 -1
  39. {lionagi-0.7.0.dist-info → lionagi-0.7.1.dist-info}/METADATA +1 -1
  40. {lionagi-0.7.0.dist-info → lionagi-0.7.1.dist-info}/RECORD +42 -42
  41. {lionagi-0.7.0.dist-info → lionagi-0.7.1.dist-info}/WHEEL +0 -0
  42. {lionagi-0.7.0.dist-info → lionagi-0.7.1.dist-info}/licenses/LICENSE +0 -0
@@ -2,6 +2,12 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ """
6
+ Defines `Package` and `PackageCategory`, encapsulating the contents
7
+ and classification of mail items. Also includes a simple validator
8
+ to ensure categories are valid.
9
+ """
10
+
5
11
  from enum import Enum
6
12
  from typing import Any
7
13
 
@@ -12,7 +18,20 @@ from .._concepts import Communicatable, Observable
12
18
 
13
19
 
14
20
  class PackageCategory(str, Enum):
15
- """Enumeration of package categories in the Lion framework."""
21
+ """
22
+ Enumeration of common package categories in LionAGI:
23
+
24
+ - MESSAGE: General message content
25
+ - TOOL: A tool or action to be invoked
26
+ - IMODEL: Some internal model reference
27
+ - NODE: A node in a graph
28
+ - NODE_LIST: A list of nodes
29
+ - NODE_ID: A node ID
30
+ - START: A 'start' signal
31
+ - END: An 'end' signal
32
+ - CONDITION: A condition or gating logic
33
+ - SIGNAL: A more generic signal or marker
34
+ """
16
35
 
17
36
  MESSAGE = "message"
18
37
  TOOL = "tool"
@@ -27,7 +46,24 @@ class PackageCategory(str, Enum):
27
46
 
28
47
 
29
48
  def validate_category(value: Any) -> PackageCategory:
30
- """Validate the category field."""
49
+ """
50
+ Validate and convert the input to a valid PackageCategory.
51
+
52
+ Parameters
53
+ ----------
54
+ value : Any
55
+ The input to interpret as a `PackageCategory`.
56
+
57
+ Returns
58
+ -------
59
+ PackageCategory
60
+ The validated category.
61
+
62
+ Raises
63
+ ------
64
+ ValueError
65
+ If the value cannot be converted into a valid package category.
66
+ """
31
67
  if isinstance(value, PackageCategory):
32
68
  return value
33
69
  try:
@@ -37,6 +73,23 @@ def validate_category(value: Any) -> PackageCategory:
37
73
 
38
74
 
39
75
  class Package(Observable):
76
+ """
77
+ A self-contained package that can be attached to `Mail` items.
78
+ Includes a unique ID, creation timestamp, category, payload item,
79
+ and an optional request source for context.
80
+
81
+ Attributes
82
+ ----------
83
+ category : PackageCategory
84
+ The classification or type of package.
85
+ item : Any
86
+ The main payload or data of this package.
87
+ request_source : ID[Communicatable] | None
88
+ An optional reference indicating the origin or context
89
+ for this package.
90
+ """
91
+
92
+ __slots__ = ("id", "created_at", "category", "item", "request_source")
40
93
 
41
94
  def __init__(
42
95
  self,
@@ -52,4 +105,5 @@ class Package(Observable):
52
105
  self.item = item
53
106
  self.request_source = request_source
54
107
 
55
- __slots__ = ("id", "created_at", "category", "item", "request_source")
108
+
109
+ # File: lionagi/protocols/mail/package.py
@@ -2,6 +2,12 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ """
6
+ Defines the `ActionRequest` class, a specific `RoledMessage` for requesting
7
+ a function or action call within LionAGI. It is typically accompanied by
8
+ arguments and can later be answered by an `ActionResponse`.
9
+ """
10
+
5
11
  from collections.abc import Callable
6
12
  from typing import Any
7
13
 
@@ -16,7 +22,23 @@ def prepare_action_request(
16
22
  function: str | Callable,
17
23
  arguments: dict,
18
24
  ) -> dict[str, Any]:
19
-
25
+ """
26
+ Build a structured dict describing the request details.
27
+
28
+ Args:
29
+ function (str | Callable):
30
+ The name (or callable) representing the function to invoke.
31
+ arguments (dict):
32
+ The arguments necessary for the function call.
33
+
34
+ Returns:
35
+ dict[str, Any]: A standardized dictionary containing
36
+ 'action_request' -> {'function':..., 'arguments':...}
37
+
38
+ Raises:
39
+ ValueError: If `function` is neither a string nor callable, or
40
+ if `arguments` cannot be turned into a dictionary.
41
+ """
20
42
  if isinstance(function, Callable):
21
43
  function = function.__name__
22
44
  if hasattr(function, "function"):
@@ -36,6 +58,11 @@ def prepare_action_request(
36
58
 
37
59
 
38
60
  class ActionRequest(RoledMessage):
61
+ """
62
+ A message that requests an action or function to be executed.
63
+ It inherits from `RoledMessage` and includes function name,
64
+ arguments, and optional linking to a subsequent `ActionResponse`.
65
+ """
39
66
 
40
67
  template: Template | str | None = jinja_env.get_template(
41
68
  "action_request.jinja2"
@@ -44,50 +71,44 @@ class ActionRequest(RoledMessage):
44
71
  @property
45
72
  def action_response_id(self) -> IDType | None:
46
73
  """
47
- Get the ID of the corresponding action response.
74
+ Get or set the ID of the corresponding action response.
48
75
 
49
76
  Returns:
50
- IDType | None: The ID of the action response, or None if not responded
77
+ IDType | None: The ID of the action response, or None if none assigned.
51
78
  """
52
79
  return self.content.get("action_response_id", None)
53
80
 
54
81
  @action_response_id.setter
55
82
  def action_response_id(self, action_response_id: IDType) -> None:
56
- """
57
- Set the ID of the corresponding action response.
58
-
59
- Args:
60
- action_response_id: The ID of the action response
61
- """
62
83
  self.content["action_response_id"] = action_response_id
63
84
 
64
85
  @property
65
86
  def request(self) -> dict[str, Any]:
66
87
  """
67
- Get the action request content as a dictionary.
88
+ Get the entire 'action_request' dictionary if present.
68
89
 
69
90
  Returns:
70
- dict[str, Any]: The request content excluding output
91
+ dict[str, Any]: The request content or empty dict if missing.
71
92
  """
72
93
  return copy(self.content.get("action_request", {}))
73
94
 
74
95
  @property
75
96
  def arguments(self) -> dict[str, Any]:
76
97
  """
77
- Get the arguments for the action request.
98
+ Access just the 'arguments' from the action request.
78
99
 
79
100
  Returns:
80
- dict[str, Any]: The arguments dictionary
101
+ dict[str, Any]: The arguments to be used by the function call.
81
102
  """
82
103
  return self.request.get("arguments", {})
83
104
 
84
105
  @property
85
106
  def function(self) -> str:
86
107
  """
87
- Get the function name for the action request.
108
+ Name of the function to be invoked.
88
109
 
89
110
  Returns:
90
- str: The name of the function to be invoked
111
+ str: The function name or empty string if none provided.
91
112
  """
92
113
  return self.request.get("function", "")
93
114
 
@@ -102,16 +123,24 @@ class ActionRequest(RoledMessage):
102
123
  **kwargs,
103
124
  ) -> "ActionRequest":
104
125
  """
105
- Create a new ActionRequest instance.
126
+ Build a new ActionRequest.
106
127
 
107
128
  Args:
108
- function: The function to be invoked
109
- arguments: The arguments to be passed to the function
110
- sender: The sender of the request
111
- recipient: The recipient of the request
129
+ function (str | Callable | None):
130
+ The function or callable name.
131
+ arguments (dict | None):
132
+ Arguments for that function call.
133
+ sender (SenderRecipient | None):
134
+ The sender identifier or role.
135
+ recipient (SenderRecipient | None):
136
+ The recipient identifier or role.
137
+ template (Template | str | None):
138
+ Optional custom template.
139
+ **kwargs:
140
+ Extra key-value pairs to merge into the content.
112
141
 
113
142
  Returns:
114
- ActionRequest: The new instance
143
+ ActionRequest: A newly constructed instance.
115
144
  """
116
145
  content = prepare_action_request(function, arguments)
117
146
  content.update(kwargs)
@@ -135,15 +164,36 @@ class ActionRequest(RoledMessage):
135
164
  template: Template | str | None = None,
136
165
  **kwargs,
137
166
  ):
167
+ """
168
+ Update this request with new function, arguments, or link to an
169
+ action response.
170
+
171
+ Args:
172
+ function (str): New function name, if changing.
173
+ arguments (dict): New arguments dictionary, if changing.
174
+ sender (SenderRecipient): New sender.
175
+ recipient (SenderRecipient): New recipient.
176
+ action_response (ActionResponse):
177
+ If provided, this request is flagged as responded.
178
+ template (Template | str | None):
179
+ Optional new template.
180
+ **kwargs:
181
+ Additional fields to store in content.
182
+
183
+ Raises:
184
+ ValueError: If the request is already responded to.
185
+ """
138
186
  if self.is_responded():
139
187
  raise ValueError("Cannot update a responded action request.")
140
188
 
189
+ # Link action response if given
141
190
  if (
142
191
  isinstance(action_response, RoledMessage)
143
192
  and action_response.class_name() == "ActionResponse"
144
193
  ):
145
194
  self.action_response_id = action_response.id
146
195
 
196
+ # If new function or arguments, create new 'action_request' content
147
197
  if any([function, arguments]):
148
198
  action_request = prepare_action_request(
149
199
  function or self.function, arguments or self.arguments
@@ -155,11 +205,12 @@ class ActionRequest(RoledMessage):
155
205
 
156
206
  def is_responded(self) -> bool:
157
207
  """
158
- Check if the action request has been responded to.
208
+ Check if there's a linked action response.
159
209
 
160
210
  Returns:
161
- bool: True if the request has a response, False otherwise
211
+ bool: True if an action response ID is present.
162
212
  """
163
- if self.action_response_id is not None:
164
- return True
165
- return False
213
+ return self.action_response_id is not None
214
+
215
+
216
+ # File: lionagi/protocols/messages/action_request.py
@@ -2,6 +2,11 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ """
6
+ Defines `ActionResponse`, an `RoledMessage` that answers an `ActionRequest`
7
+ with output from a function call or action.
8
+ """
9
+
5
10
  from typing import Any
6
11
 
7
12
  from typing_extensions import override
@@ -18,6 +23,16 @@ def prepare_action_response_content(
18
23
  action_request: ActionRequest,
19
24
  output: Any,
20
25
  ) -> dict:
26
+ """
27
+ Convert an ActionRequest + function output into response-friendly dictionary.
28
+
29
+ Args:
30
+ action_request (ActionRequest): The original action request.
31
+ output (Any): The result of the function call.
32
+
33
+ Returns:
34
+ dict: A dictionary containing `action_request_id` and `action_response`.
35
+ """
21
36
  return {
22
37
  "action_request_id": str(action_request.id),
23
38
  "action_response": {
@@ -29,6 +44,10 @@ def prepare_action_response_content(
29
44
 
30
45
 
31
46
  class ActionResponse(RoledMessage):
47
+ """
48
+ A message fulfilling an `ActionRequest`. It stores the function name,
49
+ the arguments used, and the output produced by the function.
50
+ """
32
51
 
33
52
  template: Template | str | None = jinja_env.get_template(
34
53
  "action_response.jinja2"
@@ -36,52 +55,32 @@ class ActionResponse(RoledMessage):
36
55
 
37
56
  @property
38
57
  def function(self) -> str:
39
- """
40
- Get the function name from the action response.
41
-
42
- Returns:
43
- str: The name of the function that was executed
44
- """
58
+ """Name of the function that was executed."""
45
59
  return self.content.get("action_response", {}).get("function", None)
46
60
 
47
61
  @property
48
62
  def arguments(self) -> dict[str, Any]:
49
- """
50
- Get the function arguments from the action response.
51
-
52
- Returns:
53
- dict[str, Any]: The arguments that were used
54
- """
63
+ """Arguments used for the executed function."""
55
64
  return self.content.get("action_response", {}).get("arguments", {})
56
65
 
57
66
  @property
58
67
  def output(self) -> Any:
59
- """
60
- Get the function output from the action response.
61
-
62
- Returns:
63
- Any: The result returned by the function
64
- """
68
+ """The result or returned data from the function call."""
65
69
  return self.content.get("action_response", {}).get("output", None)
66
70
 
67
71
  @property
68
72
  def response(self) -> dict[str, Any]:
69
73
  """
70
- Get the complete action response as a dictionary.
74
+ A helper to get the entire 'action_response' dictionary.
71
75
 
72
76
  Returns:
73
- dict[str, Any]: The response including function details and output
77
+ dict[str, Any]: The entire response, including function, arguments, and output.
74
78
  """
75
79
  return copy(self.content.get("action_response", {}))
76
80
 
77
81
  @property
78
82
  def action_request_id(self) -> IDType:
79
- """
80
- Get the ID of the corresponding action request.
81
-
82
- Returns:
83
- ID[ActionRequest].ID | None: The ID of the original request
84
- """
83
+ """The ID of the original action request."""
85
84
  return IDType.validate(self.content.get("action_request_id"))
86
85
 
87
86
  @override
@@ -94,7 +93,22 @@ class ActionResponse(RoledMessage):
94
93
  sender: SenderRecipient | None = None,
95
94
  recipient: SenderRecipient | None = None,
96
95
  ) -> "ActionResponse":
96
+ """
97
+ Build an ActionResponse from a matching `ActionRequest` and output.
98
+
99
+ Args:
100
+ action_request (ActionRequest): The original request being fulfilled.
101
+ output (Any, optional): The function output or result.
102
+ response_model (Any, optional):
103
+ If present and has `.output`, this is used instead of `output`.
104
+ sender (SenderRecipient, optional):
105
+ The role or ID of the sender (defaults to the request's recipient).
106
+ recipient (SenderRecipient, optional):
107
+ The role or ID of the recipient (defaults to the request's sender).
97
108
 
109
+ Returns:
110
+ ActionResponse: A new instance referencing the `ActionRequest`.
111
+ """
98
112
  if response_model:
99
113
  output = response_model.output
100
114
 
@@ -119,6 +133,18 @@ class ActionResponse(RoledMessage):
119
133
  template: Template | str | None = None,
120
134
  **kwargs,
121
135
  ):
136
+ """
137
+ Update this response with a new request reference or new output.
138
+
139
+ Args:
140
+ action_request (ActionRequest): The updated request.
141
+ output (Any): The new function output data.
142
+ response_model: If present, uses response_model.output.
143
+ sender (SenderRecipient): New sender ID or role.
144
+ recipient (SenderRecipient): New recipient ID or role.
145
+ template (Template | str | None): Optional new template.
146
+ **kwargs: Additional fields to store in content.
147
+ """
122
148
  if response_model:
123
149
  output = response_model.output
124
150
 
@@ -130,3 +156,6 @@ class ActionResponse(RoledMessage):
130
156
  super().update(
131
157
  sender=sender, recipient=recipient, template=template, **kwargs
132
158
  )
159
+
160
+
161
+ # File: lionagi/protocols/messages/action_response.py
@@ -2,6 +2,10 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ """
6
+ Defines `AssistantResponse`, a specialized `RoledMessage` for the AI's
7
+ assistant replies (usually from LLM or related).
8
+ """
5
9
  from typing import Any
6
10
 
7
11
  from pydantic import BaseModel
@@ -69,6 +73,11 @@ def prepare_assistant_response(
69
73
 
70
74
 
71
75
  class AssistantResponse(RoledMessage):
76
+ """
77
+ A message representing the AI assistant's reply, typically
78
+ from a model or LLM call. If the raw model output is available,
79
+ it's placed in `metadata["model_response"]`.
80
+ """
72
81
 
73
82
  template: Template | str | None = jinja_env.get_template(
74
83
  "assistant_response.jinja2"
@@ -76,31 +85,20 @@ class AssistantResponse(RoledMessage):
76
85
 
77
86
  @property
78
87
  def response(self) -> str:
79
- """
80
- Get the assistant response content.
81
-
82
- Returns:
83
- str: The formatted content of the assistant's response
84
- """
88
+ """Get or set the text portion of the assistant's response."""
85
89
  return copy(self.content["assistant_response"])
86
90
 
87
91
  @response.setter
88
92
  def response(self, value: str) -> None:
89
- """
90
- Set the assistant response content.
91
-
92
- Args:
93
- value: The new response content
94
- """
95
93
  self.content["assistant_response"] = value
96
94
 
97
95
  @property
98
96
  def model_response(self) -> dict | list[dict]:
99
97
  """
100
- Get the underlying model response data.
98
+ Access the underlying model's raw data, if available.
101
99
 
102
100
  Returns:
103
- Union[dict, List[dict]]: The complete model response data
101
+ dict or list[dict]: The stored model output data.
104
102
  """
105
103
  return copy(self.metadata.get("model_response", {}))
106
104
 
@@ -112,7 +110,26 @@ class AssistantResponse(RoledMessage):
112
110
  recipient: SenderRecipient | None = None,
113
111
  template: Template | str | None = None,
114
112
  **kwargs,
115
- ):
113
+ ) -> "AssistantResponse":
114
+ """
115
+ Build an AssistantResponse from arbitrary assistant data.
116
+
117
+ Args:
118
+ assistant_response:
119
+ A pydantic model, list, dict, or string representing
120
+ an LLM or system response.
121
+ sender (SenderRecipient | None):
122
+ The ID or role denoting who sends this response.
123
+ recipient (SenderRecipient | None):
124
+ The ID or role to receive it.
125
+ template (Template | str | None):
126
+ Optional custom template.
127
+ **kwargs:
128
+ Additional content key-value pairs.
129
+
130
+ Returns:
131
+ AssistantResponse: The constructed instance.
132
+ """
116
133
  content = prepare_assistant_response(assistant_response)
117
134
  model_response = content.pop("model_response", {})
118
135
  content.update(kwargs)
@@ -139,9 +156,27 @@ class AssistantResponse(RoledMessage):
139
156
  template: Template | str | None = None,
140
157
  **kwargs,
141
158
  ):
159
+ """
160
+ Update this AssistantResponse with new data or fields.
161
+
162
+ Args:
163
+ assistant_response:
164
+ Additional or replaced assistant model output.
165
+ sender (SenderRecipient | None):
166
+ Updated sender.
167
+ recipient (SenderRecipient | None):
168
+ Updated recipient.
169
+ template (Template | str | None):
170
+ Optional new template.
171
+ **kwargs:
172
+ Additional content updates for `self.content`.
173
+ """
142
174
  if assistant_response:
143
175
  content = prepare_assistant_response(assistant_response)
144
176
  self.content.update(content)
145
177
  super().update(
146
178
  sender=sender, recipient=recipient, template=template, **kwargs
147
179
  )
180
+
181
+
182
+ # File: lionagi/protocols/messages/assistant_response.py
@@ -2,6 +2,12 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ """
6
+ Holds foundational enumerations and types for messages, including
7
+ roles like `SYSTEM`, `USER`, and helper functions for validating
8
+ sender/recipient fields.
9
+ """
10
+
5
11
  from enum import Enum
6
12
  from typing import Any, TypeAlias
7
13
 
@@ -17,6 +23,9 @@ __all__ = (
17
23
 
18
24
 
19
25
  class MessageRole(str, Enum):
26
+ """
27
+ Predefined roles for conversation participants or message semantics.
28
+ """
20
29
 
21
30
  SYSTEM = "system"
22
31
  USER = "user"
@@ -26,15 +35,27 @@ class MessageRole(str, Enum):
26
35
 
27
36
 
28
37
  class MessageFlag(str, Enum):
38
+ """
39
+ Internal flags for certain message states, e.g., clones or loads.
40
+ """
29
41
 
30
42
  MESSAGE_CLONE = "MESSAGE_CLONE"
31
43
  MESSAGE_LOAD = "MESSAGE_LOAD"
32
44
 
33
45
 
34
46
  SenderRecipient: TypeAlias = IDType | MessageRole | str
47
+ """
48
+ A union type indicating that a sender or recipient could be:
49
+ - A lionagi IDType,
50
+ - A string-based role or ID,
51
+ - A specific enum role from `MessageRole`.
52
+ """
35
53
 
36
54
 
37
55
  class MessageField(str, Enum):
56
+ """
57
+ Common field names used in message objects.
58
+ """
38
59
 
39
60
  CREATED_AT = "created_at"
40
61
  ROLE = "role"
@@ -49,6 +70,18 @@ MESSAGE_FIELDS = [i.value for i in MessageField.__members__.values()]
49
70
 
50
71
 
51
72
  def validate_sender_recipient(value: Any, /) -> SenderRecipient:
73
+ """
74
+ Normalize a sender/recipient value into a recognized type.
75
+
76
+ Args:
77
+ value (Any): Input to interpret as a role or ID.
78
+
79
+ Returns:
80
+ SenderRecipient: A validated and normalized entity.
81
+
82
+ Raises:
83
+ ValueError: If the input cannot be recognized as a role or ID.
84
+ """
52
85
  if isinstance(value, MessageRole | MessageFlag):
53
86
  return value
54
87
 
@@ -71,3 +104,6 @@ def validate_sender_recipient(value: Any, /) -> SenderRecipient:
71
104
  return ID.get_id(value)
72
105
  except IDError as e:
73
106
  raise ValueError("Invalid sender or recipient") from e
107
+
108
+
109
+ # File: lionagi/protocols/messages/base.py