lionagi 0.7.0__py3-none-any.whl → 0.7.2__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 (43) hide show
  1. lionagi/operations/ReAct/ReAct.py +2 -2
  2. lionagi/operations/_act/act.py +10 -3
  3. lionagi/operations/communicate/communicate.py +0 -59
  4. lionagi/operations/interpret/interpret.py +1 -2
  5. lionagi/operations/operate/operate.py +10 -5
  6. lionagi/operations/parse/parse.py +0 -36
  7. lionagi/operations/plan/plan.py +3 -3
  8. lionagi/operatives/action/manager.py +105 -82
  9. lionagi/operatives/action/request_response_model.py +31 -0
  10. lionagi/operatives/action/tool.py +50 -20
  11. lionagi/protocols/_concepts.py +1 -1
  12. lionagi/protocols/adapters/adapter.py +25 -0
  13. lionagi/protocols/adapters/json_adapter.py +107 -27
  14. lionagi/protocols/adapters/pandas_/csv_adapter.py +55 -11
  15. lionagi/protocols/adapters/pandas_/excel_adapter.py +52 -10
  16. lionagi/protocols/adapters/pandas_/pd_dataframe_adapter.py +54 -4
  17. lionagi/protocols/adapters/pandas_/pd_series_adapter.py +40 -0
  18. lionagi/protocols/generic/element.py +1 -1
  19. lionagi/protocols/generic/pile.py +5 -8
  20. lionagi/protocols/graph/edge.py +1 -1
  21. lionagi/protocols/graph/graph.py +16 -8
  22. lionagi/protocols/graph/node.py +1 -1
  23. lionagi/protocols/mail/exchange.py +126 -15
  24. lionagi/protocols/mail/mail.py +33 -0
  25. lionagi/protocols/mail/mailbox.py +62 -0
  26. lionagi/protocols/mail/manager.py +97 -41
  27. lionagi/protocols/mail/package.py +57 -3
  28. lionagi/protocols/messages/action_request.py +77 -26
  29. lionagi/protocols/messages/action_response.py +55 -26
  30. lionagi/protocols/messages/assistant_response.py +50 -15
  31. lionagi/protocols/messages/base.py +36 -0
  32. lionagi/protocols/messages/instruction.py +175 -145
  33. lionagi/protocols/messages/manager.py +152 -56
  34. lionagi/protocols/messages/message.py +61 -25
  35. lionagi/protocols/messages/system.py +54 -19
  36. lionagi/service/imodel.py +24 -0
  37. lionagi/session/branch.py +40 -32
  38. lionagi/utils.py +1 -0
  39. lionagi/version.py +1 -1
  40. {lionagi-0.7.0.dist-info → lionagi-0.7.2.dist-info}/METADATA +1 -1
  41. {lionagi-0.7.0.dist-info → lionagi-0.7.2.dist-info}/RECORD +43 -43
  42. {lionagi-0.7.0.dist-info → lionagi-0.7.2.dist-info}/WHEEL +0 -0
  43. {lionagi-0.7.0.dist-info → lionagi-0.7.2.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