lionagi 0.6.1__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 (78) hide show
  1. lionagi/libs/token_transform/__init__.py +0 -0
  2. lionagi/libs/token_transform/llmlingua.py +1 -0
  3. lionagi/libs/token_transform/perplexity.py +439 -0
  4. lionagi/libs/token_transform/synthlang.py +409 -0
  5. lionagi/operations/ReAct/ReAct.py +126 -0
  6. lionagi/operations/ReAct/utils.py +28 -0
  7. lionagi/operations/__init__.py +1 -9
  8. lionagi/operations/_act/act.py +73 -0
  9. lionagi/operations/chat/__init__.py +3 -0
  10. lionagi/operations/chat/chat.py +173 -0
  11. lionagi/operations/communicate/__init__.py +0 -0
  12. lionagi/operations/communicate/communicate.py +108 -0
  13. lionagi/operations/instruct/__init__.py +3 -0
  14. lionagi/operations/instruct/instruct.py +29 -0
  15. lionagi/operations/interpret/__init__.py +3 -0
  16. lionagi/operations/interpret/interpret.py +39 -0
  17. lionagi/operations/operate/__init__.py +3 -0
  18. lionagi/operations/operate/operate.py +194 -0
  19. lionagi/operations/parse/__init__.py +3 -0
  20. lionagi/operations/parse/parse.py +89 -0
  21. lionagi/operations/plan/plan.py +3 -3
  22. lionagi/operations/select/__init__.py +0 -4
  23. lionagi/operations/select/select.py +11 -30
  24. lionagi/operations/select/utils.py +13 -2
  25. lionagi/operations/translate/__init__.py +0 -0
  26. lionagi/operations/translate/translate.py +47 -0
  27. lionagi/operations/types.py +16 -0
  28. lionagi/operatives/action/manager.py +115 -93
  29. lionagi/operatives/action/request_response_model.py +31 -0
  30. lionagi/operatives/action/tool.py +50 -20
  31. lionagi/operatives/strategies/__init__.py +3 -0
  32. lionagi/protocols/_concepts.py +1 -1
  33. lionagi/protocols/adapters/adapter.py +25 -0
  34. lionagi/protocols/adapters/json_adapter.py +107 -27
  35. lionagi/protocols/adapters/pandas_/csv_adapter.py +55 -11
  36. lionagi/protocols/adapters/pandas_/excel_adapter.py +52 -10
  37. lionagi/protocols/adapters/pandas_/pd_dataframe_adapter.py +54 -4
  38. lionagi/protocols/adapters/pandas_/pd_series_adapter.py +40 -0
  39. lionagi/protocols/generic/element.py +1 -1
  40. lionagi/protocols/generic/pile.py +5 -8
  41. lionagi/protocols/graph/edge.py +1 -1
  42. lionagi/protocols/graph/graph.py +16 -8
  43. lionagi/protocols/graph/node.py +1 -1
  44. lionagi/protocols/mail/exchange.py +126 -15
  45. lionagi/protocols/mail/mail.py +33 -0
  46. lionagi/protocols/mail/mailbox.py +62 -0
  47. lionagi/protocols/mail/manager.py +97 -41
  48. lionagi/protocols/mail/package.py +57 -3
  49. lionagi/protocols/messages/action_request.py +77 -26
  50. lionagi/protocols/messages/action_response.py +55 -26
  51. lionagi/protocols/messages/assistant_response.py +50 -15
  52. lionagi/protocols/messages/base.py +36 -0
  53. lionagi/protocols/messages/instruction.py +175 -145
  54. lionagi/protocols/messages/manager.py +152 -56
  55. lionagi/protocols/messages/message.py +61 -25
  56. lionagi/protocols/messages/system.py +54 -19
  57. lionagi/service/imodel.py +24 -0
  58. lionagi/session/branch.py +1116 -939
  59. lionagi/utils.py +1 -0
  60. lionagi/version.py +1 -1
  61. {lionagi-0.6.1.dist-info → lionagi-0.7.1.dist-info}/METADATA +1 -1
  62. {lionagi-0.6.1.dist-info → lionagi-0.7.1.dist-info}/RECORD +75 -56
  63. lionagi/libs/compress/models.py +0 -66
  64. lionagi/libs/compress/utils.py +0 -69
  65. lionagi/operations/select/prompt.py +0 -5
  66. /lionagi/{libs/compress → operations/ReAct}/__init__.py +0 -0
  67. /lionagi/operations/{strategies → _act}/__init__.py +0 -0
  68. /lionagi/{operations → operatives}/strategies/base.py +0 -0
  69. /lionagi/{operations → operatives}/strategies/concurrent.py +0 -0
  70. /lionagi/{operations → operatives}/strategies/concurrent_chunk.py +0 -0
  71. /lionagi/{operations → operatives}/strategies/concurrent_sequential_chunk.py +0 -0
  72. /lionagi/{operations → operatives}/strategies/params.py +0 -0
  73. /lionagi/{operations → operatives}/strategies/sequential.py +0 -0
  74. /lionagi/{operations → operatives}/strategies/sequential_chunk.py +0 -0
  75. /lionagi/{operations → operatives}/strategies/sequential_concurrent_chunk.py +0 -0
  76. /lionagi/{operations → operatives}/strategies/utils.py +0 -0
  77. {lionagi-0.6.1.dist-info → lionagi-0.7.1.dist-info}/WHEEL +0 -0
  78. {lionagi-0.6.1.dist-info → lionagi-0.7.1.dist-info}/licenses/LICENSE +0 -0
@@ -994,8 +994,8 @@ class Pile(Element, Collective[E], Generic[E]):
994
994
  path_or_buf,
995
995
  *,
996
996
  use_pd: bool = False,
997
+ many: bool = False,
997
998
  mode="w",
998
- verbose=False,
999
999
  **kwargs,
1000
1000
  ):
1001
1001
  """Export collection to JSON file.
@@ -1007,15 +1007,9 @@ class Pile(Element, Collective[E], Generic[E]):
1007
1007
  verbose: Print confirmation message.
1008
1008
  **kwargs: Additional arguments for json.dump() or DataFrame.to_json().
1009
1009
  """
1010
-
1011
1010
  if use_pd:
1012
1011
  return self.to_df().to_json(mode=mode, **kwargs)
1013
- dict_ = self.to_dict()
1014
- with open(path_or_buf, mode) as f:
1015
- json.dump(dict_, f, **kwargs)
1016
-
1017
- if verbose:
1018
- print(f"Saved Pile to {path_or_buf}")
1012
+ return self.adapt_to(".json", fp=path_or_buf, mode=mode, many=many)
1019
1013
 
1020
1014
 
1021
1015
  def pile(
@@ -1076,3 +1070,6 @@ def to_list_type(value: Any, /) -> list[Any]:
1076
1070
  if isinstance(value, list | tuple | set | deque | Generator):
1077
1071
  return list(value)
1078
1072
  return [value]
1073
+
1074
+
1075
+ # File: lionagi/protocols/generic/pile.py
@@ -163,4 +163,4 @@ class Edge(Element):
163
163
  cond.source = source
164
164
 
165
165
 
166
- # File: protocols/graph/edge.py
166
+ # File: lionagi/protocols/graph/edge.py
@@ -204,13 +204,14 @@ class Graph(Element, Relational):
204
204
  def to_networkx(self, **kwargs) -> Any:
205
205
  """Convert the graph to a NetworkX graph object."""
206
206
  try:
207
- import networkx as nx # type: ignore
207
+ from networkx import DiGraph # type: ignore
208
+
208
209
  except ImportError:
209
- raise ImportError(
210
- "NetworkX is not installed. Please install it with `pip install networkx`."
211
- )
210
+ from lionagi.libs.package.imports import check_import
212
211
 
213
- g = nx.DiGraph(**kwargs)
212
+ DiGraph = check_import("networkx", import_name="DiGraph")
213
+
214
+ g = DiGraph(**kwargs)
214
215
  for node in self.internal_nodes:
215
216
  node_info = node.to_dict()
216
217
  node_info.pop("id")
@@ -238,9 +239,13 @@ class Graph(Element, Relational):
238
239
  import matplotlib.pyplot as plt # type: ignore
239
240
  import networkx as nx # type: ignore
240
241
  except ImportError:
241
- raise ImportError(
242
- "Failed to import Matplotlib. Please install the package with `pip install matplotlib`"
243
- )
242
+ from lionagi.libs.package.imports import check_import
243
+
244
+ check_import("matplotlib")
245
+ check_import("networkx")
246
+
247
+ import matplotlib.pyplot as plt # type: ignore
248
+ import networkx as nx # type: ignore
244
249
 
245
250
  g = self.to_networkx(**kwargs)
246
251
  pos = nx.spring_layout(g)
@@ -288,3 +293,6 @@ class Graph(Element, Relational):
288
293
 
289
294
  def __contains__(self, item: object) -> bool:
290
295
  return item in self.internal_nodes or item in self.internal_edges
296
+
297
+
298
+ # File: lionagi/protocols/graph/graph.py
@@ -122,4 +122,4 @@ class Node(Element, Relational):
122
122
  return cls._get_adapter_registry().list_adapters()
123
123
 
124
124
 
125
- # File: protocols/graph/node.py
125
+ # File: lionagi/protocols/graph/node.py
@@ -2,6 +2,12 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ """
6
+ Provides the `Exchange` class, which orchestrates mail flows among
7
+ sources that implement `Communicatable`. It collects pending outgoing
8
+ mail from each source and delivers them to the appropriate recipients.
9
+ """
10
+
5
11
  import asyncio
6
12
  from typing import Any
7
13
 
@@ -15,8 +21,34 @@ from .mailbox import Mailbox
15
21
 
16
22
 
17
23
  class Exchange:
24
+ """
25
+ Manages mail exchange operations among a set of sources that are
26
+ `Communicatable`. Each source has an associated `Mailbox` to store
27
+ inbound and outbound mail.
28
+
29
+ Attributes
30
+ ----------
31
+ sources : Pile[Communicatable]
32
+ The communicatable sources participating in the exchange.
33
+ buffer : dict[IDType, list[Mail]]
34
+ A temporary holding area for mail messages before they reach
35
+ their recipient's mailbox.
36
+ mailboxes : dict[IDType, Mailbox]
37
+ Maps each source's ID to its Mailbox.
38
+ _execute_stop : bool
39
+ A flag indicating whether to stop the asynchronous execution loop.
40
+ """
18
41
 
19
42
  def __init__(self, sources: ID[Communicatable].ItemSeq = None):
43
+ """
44
+ Initialize an `Exchange` instance.
45
+
46
+ Parameters
47
+ ----------
48
+ sources : ID[Communicatable].ItemSeq, optional
49
+ One or more communicatable sources to manage. If provided,
50
+ they are immediately added.
51
+ """
20
52
  self.sources: Pile[Communicatable] = Pile(
21
53
  item_type={Communicatable}, strict_type=False
22
54
  )
@@ -26,7 +58,20 @@ class Exchange:
26
58
  self.add_source(sources)
27
59
  self._execute_stop: bool = False
28
60
 
29
- def add_source(self, sources: ID[Communicatable].ItemSeq):
61
+ def add_source(self, sources: ID[Communicatable].ItemSeq) -> None:
62
+ """
63
+ Register new communicatable sources for mail exchange.
64
+
65
+ Parameters
66
+ ----------
67
+ sources : ID[Communicatable].ItemSeq
68
+ The source(s) to be added.
69
+
70
+ Raises
71
+ ------
72
+ ValueError
73
+ If the given sources already exist in this exchange.
74
+ """
30
75
  if sources in self.sources:
31
76
  raise ValueError(
32
77
  f"Source {sources} already exists in the mail manager."
@@ -37,8 +82,21 @@ class Exchange:
37
82
  self.mailboxes[source.id] = source.mailbox
38
83
  self.buffer.update({source.id: [] for source in sources})
39
84
 
40
- def delete_source(self, sources: ID[Communicatable].ItemSeq):
41
- """this will remove the source from the mail manager and all assosiated pending mails"""
85
+ def delete_source(self, sources: ID[Communicatable].ItemSeq) -> None:
86
+ """
87
+ Remove specified sources from the exchange, clearing any pending
88
+ mail associated with them.
89
+
90
+ Parameters
91
+ ----------
92
+ sources : ID[Communicatable].ItemSeq
93
+ The source(s) to remove.
94
+
95
+ Raises
96
+ ------
97
+ ValueError
98
+ If the given sources do not exist in this exchange.
99
+ """
42
100
  if not sources in self.sources:
43
101
  raise ValueError(
44
102
  f"Source {sources} does not exist in the mail manager."
@@ -57,13 +115,47 @@ class Exchange:
57
115
  item: Any,
58
116
  request_source: Any = None,
59
117
  ) -> Mail:
118
+ """
119
+ Helper method to create a new Mail instance.
120
+
121
+ Parameters
122
+ ----------
123
+ sender : ID[Communicatable]
124
+ The ID (or Communicatable) identifying the mail sender.
125
+ recipient : ID[Communicatable]
126
+ The ID (or Communicatable) identifying the mail recipient.
127
+ category : PackageCategory | str
128
+ A classification for the package contents.
129
+ item : Any
130
+ The actual item/data to be sent.
131
+ request_source : Any, optional
132
+ Additional context about the request origin, if any.
133
+
134
+ Returns
135
+ -------
136
+ Mail
137
+ A newly created Mail object ready for sending.
138
+ """
60
139
  package = Package(
61
140
  category=category, item=item, request_source=request_source
62
141
  )
63
142
  return Mail(sender=sender, recipient=recipient, package=package)
64
143
 
65
- def collect(self, sender: ID[Communicatable]):
66
- """collect all mails from a particular sender"""
144
+ def collect(self, sender: ID[Communicatable]) -> None:
145
+ """
146
+ Collect all outbound mail from a specific sender, moving it
147
+ to the exchange buffer.
148
+
149
+ Parameters
150
+ ----------
151
+ sender : ID[Communicatable]
152
+ The ID of the source from which mail is collected.
153
+
154
+ Raises
155
+ ------
156
+ ValueError
157
+ If the sender is not part of this exchange.
158
+ """
67
159
  if sender not in self.sources:
68
160
  raise ValueError(f"Sender source {sender} does not exist.")
69
161
 
@@ -73,7 +165,21 @@ class Exchange:
73
165
  mail: Mail = sender_mailbox.pile_.popleft()
74
166
  self.buffer[mail.recipient].append(mail)
75
167
 
76
- def deliver(self, recipient: ID[Communicatable]):
168
+ def deliver(self, recipient: ID[Communicatable]) -> None:
169
+ """
170
+ Deliver all mail in the buffer addressed to a specific recipient.
171
+
172
+ Parameters
173
+ ----------
174
+ recipient : ID[Communicatable]
175
+ The ID of the source to receive mail.
176
+
177
+ Raises
178
+ ------
179
+ ValueError
180
+ If the recipient is not part of this exchange or if mail
181
+ references an unknown sender.
182
+ """
77
183
  if recipient not in self.sources:
78
184
  raise ValueError(f"Recipient source {recipient} does not exist.")
79
185
 
@@ -90,27 +196,32 @@ class Exchange:
90
196
  recipient_mailbox.append_in(mail)
91
197
 
92
198
  def collect_all(self) -> None:
93
- """Collect mail from all sources."""
199
+ """
200
+ Collect mail from every source in this exchange.
201
+ """
94
202
  for source in self.sources:
95
203
  self.collect(sender=source.id)
96
204
 
97
205
  def deliver_all(self) -> None:
98
- """Send mail to all recipients."""
206
+ """
207
+ Deliver mail to every source in this exchange.
208
+ """
99
209
  for source in self.sources:
100
210
  self.deliver(recipient=source.id)
101
211
 
102
212
  async def execute(self, refresh_time: int = 1) -> None:
103
213
  """
104
- Execute mail collection and sending process asynchronously.
105
-
106
- This method runs in a loop, collecting and sending mail at
107
- regular intervals.
214
+ Continuously collect and deliver mail in an asynchronous loop.
108
215
 
109
- Args:
110
- refresh_time (int, optional): The time to wait between
111
- each cycle in seconds. Defaults to 1.
216
+ Parameters
217
+ ----------
218
+ refresh_time : int, optional
219
+ Number of seconds to wait between each cycle. Defaults to 1.
112
220
  """
113
221
  while not self._execute_stop:
114
222
  self.collect_all()
115
223
  self.deliver_all()
116
224
  await asyncio.sleep(refresh_time)
225
+
226
+
227
+ # File: lionagi/protocols/mail/exchange.py
@@ -2,6 +2,12 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ """
6
+ Defines the `Mail` class, which is a `Sendable` element representing
7
+ a single piece of mail, carrying a `Package` between a sender
8
+ and recipient.
9
+ """
10
+
5
11
  from pydantic import field_validator
6
12
 
7
13
  from .._concepts import Sendable
@@ -11,6 +17,20 @@ from .package import Package, PackageCategory
11
17
 
12
18
 
13
19
  class Mail(Element, Sendable):
20
+ """
21
+ A single mail message that can be sent between communicatable entities.
22
+ It includes a sender, recipient, and a package that describes the
23
+ mail's content.
24
+
25
+ Attributes
26
+ ----------
27
+ sender : IDType
28
+ The ID representing the mail sender.
29
+ recipient : IDType
30
+ The ID representing the mail recipient.
31
+ package : Package
32
+ The package (category + payload) contained in this mail.
33
+ """
14
34
 
15
35
  sender: IDType
16
36
  recipient: IDType
@@ -18,8 +38,21 @@ class Mail(Element, Sendable):
18
38
 
19
39
  @field_validator("sender", "recipient")
20
40
  def _validate_sender_recipient(cls, value):
41
+ """
42
+ Validate that the sender and recipient fields are correct IDTypes.
43
+ """
21
44
  return IDType.validate(value)
22
45
 
23
46
  @property
24
47
  def category(self) -> PackageCategory:
48
+ """
49
+ Shortcut for retrieving the category from the underlying package.
50
+
51
+ Returns
52
+ -------
53
+ PackageCategory
54
+ """
25
55
  return self.package.category
56
+
57
+
58
+ # File: lionagi/protocols/mail/mail.py
@@ -2,6 +2,11 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ """
6
+ Implements a simple mailbox system for each Communicatable entity.
7
+ Holds inbound and outbound mail, stored internally in a `Pile`.
8
+ """
9
+
5
10
  from lionagi.protocols.generic.element import IDType
6
11
 
7
12
  from ..generic.pile import Pile, Progression
@@ -11,37 +16,94 @@ __all__ = ("Mailbox",)
11
16
 
12
17
 
13
18
  class Mailbox:
19
+ """
20
+ A mailbox that accumulates inbound and outbound mail for a single
21
+ communicatable source.
22
+
23
+ Attributes
24
+ ----------
25
+ pile_ : Pile[Mail]
26
+ A concurrency-safe collection storing all mail items.
27
+ pending_ins : dict[IDType, Progression]
28
+ Maps each sender's ID to a progression of inbound mail.
29
+ pending_outs : Progression
30
+ A progression of mail items waiting to be sent (outbound).
31
+ """
14
32
 
15
33
  def __init__(self):
34
+ """
35
+ Initialize an empty Mailbox with separate tracks for inbound
36
+ and outbound mail.
37
+ """
16
38
  self.pile_ = Pile(item_type=Mail, strict_type=True)
17
39
  self.pending_ins: dict[IDType, Progression] = {}
18
40
  self.pending_outs = Progression()
19
41
 
20
42
  def __contains__(self, item: Mail) -> bool:
43
+ """
44
+ Check if a mail item is currently in this mailbox.
45
+ """
21
46
  return item in self.pile_
22
47
 
23
48
  @property
24
49
  def senders(self) -> list[str]:
50
+ """
51
+ List of sender IDs that have inbound mail in this mailbox.
52
+
53
+ Returns
54
+ -------
55
+ list[str]
56
+ """
25
57
  return list(self.pending_ins.keys())
26
58
 
27
59
  def append_in(self, item: Mail, /):
60
+ """
61
+ Add a mail item to the inbound queue for the item's sender.
62
+
63
+ Parameters
64
+ ----------
65
+ item : Mail
66
+ """
28
67
  if item.sender not in self.pending_ins:
29
68
  self.pending_ins[item.sender] = Progression()
30
69
  self.pending_ins[item.sender].include(item)
31
70
  self.pile_.include(item)
32
71
 
33
72
  def append_out(self, item: Mail, /):
73
+ """
74
+ Add a mail item to the outbound (pending_outs) queue.
75
+
76
+ Parameters
77
+ ----------
78
+ item : Mail
79
+ """
34
80
  self.pending_outs.include(item)
35
81
  self.pile_.include(item)
36
82
 
37
83
  def exclude(self, item: Mail, /):
84
+ """
85
+ Remove a mail item from all internal references (inbound, outbound, and pile).
86
+
87
+ Parameters
88
+ ----------
89
+ item : Mail
90
+ """
38
91
  self.pile_.exclude(item)
39
92
  self.pending_outs.exclude(item)
40
93
  for v in self.pending_ins.values():
41
94
  v.exclude(item)
42
95
 
43
96
  def __bool__(self) -> bool:
97
+ """
98
+ Indicates if the mailbox contains any mail.
99
+ """
44
100
  return bool(self.pile_)
45
101
 
46
102
  def __len__(self) -> int:
103
+ """
104
+ Number of mail items in this mailbox.
105
+ """
47
106
  return len(self.pile_)
107
+
108
+
109
+ # File: lionagi/protocols/mail/mailbox.py
@@ -2,6 +2,11 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ """
6
+ Defines the `MailManager` class, which coordinates mail operations
7
+ across multiple sources in a more abstract or high-level manner.
8
+ """
9
+
5
10
  import asyncio
6
11
  from collections import deque
7
12
  from typing import Any
@@ -17,19 +22,29 @@ from .mail import Mail, Package, PackageCategory
17
22
 
18
23
  class MailManager(Manager):
19
24
  """
20
- Manages mail operations for multiple sources in the Lion framework.
21
-
22
- This class handles the collection, distribution, and management of mail
23
- between different sources within the system.
25
+ A manager for mail operations across various observable sources
26
+ within LionAGI. Unlike `Exchange`, this class can manage the state
27
+ of multiple sources in a more general or higher-level context,
28
+ storing mail queues in a dictionary rather than individual buffers.
29
+
30
+ Attributes
31
+ ----------
32
+ sources : Pile[Observable]
33
+ A concurrency-safe collection of known sources.
34
+ mails : dict[str, dict[str, deque]]
35
+ A nested mapping of recipient -> sender -> queue of mail.
36
+ execute_stop : bool
37
+ Controls the asynchronous execution loop; set to True to exit.
24
38
  """
25
39
 
26
40
  def __init__(self, sources: ID.Item | ID.ItemSeq = None) -> None:
27
41
  """
28
42
  Initialize a MailManager instance.
29
43
 
30
- Args:
31
- sources (List[Any], optional): Initial list of mail sources to
32
- manage.
44
+ Parameters
45
+ ----------
46
+ sources : ID.Item | ID.ItemSeq, optional
47
+ Initial source(s) to manage. Each source must be an Observable.
33
48
  """
34
49
  self.sources: Pile[Observable] = Pile()
35
50
  self.mails: dict[str, dict[str, deque]] = {}
@@ -40,14 +55,17 @@ class MailManager(Manager):
40
55
 
41
56
  def add_sources(self, sources: ID.Item | ID.ItemSeq, /) -> None:
42
57
  """
43
- Add new sources to the MailManager.
58
+ Register new sources in the MailManager.
44
59
 
45
- Args:
46
- sources (Any): The sources to add. Can be a single source or
47
- a list of sources.
60
+ Parameters
61
+ ----------
62
+ sources : ID.Item | ID.ItemSeq
63
+ A single source or multiple sources to be added.
48
64
 
49
- Raises:
50
- LionValueError: If adding the source(s) fails.
65
+ Raises
66
+ ------
67
+ ValueError
68
+ If adding the sources fails for any reason.
51
69
  """
52
70
  try:
53
71
  sources = to_list_type(sources)
@@ -66,17 +84,25 @@ class MailManager(Manager):
66
84
  request_source: Any = None,
67
85
  ) -> Mail:
68
86
  """
69
- Create a new Mail object.
70
-
71
- Args:
72
- sender (str): The ID of the sender.
73
- recipient (str): The ID of the recipient.
74
- category (str): The category of the mail.
75
- package (Any): The content of the package.
76
- request_source (Any, optional): The source of the request.
77
-
78
- Returns:
79
- Mail: A new Mail object.
87
+ Factory method to generate a Mail object.
88
+
89
+ Parameters
90
+ ----------
91
+ sender : ID.Ref
92
+ Reference (ID or object) for the sender.
93
+ recipient : ID.Ref
94
+ Reference (ID or object) for the recipient.
95
+ category : PackageCategory | str
96
+ The category of this package.
97
+ package : Any
98
+ The payload or content in the mail.
99
+ request_source : Any, optional
100
+ Additional context about the request source.
101
+
102
+ Returns
103
+ -------
104
+ Mail
105
+ A new mail object with specified sender, recipient, and package.
80
106
  """
81
107
  pack = Package(
82
108
  category=category, package=package, request_source=request_source
@@ -85,13 +111,17 @@ class MailManager(Manager):
85
111
 
86
112
  def delete_source(self, source_id: IDType) -> None:
87
113
  """
88
- Delete a source from the MailManager.
114
+ Remove a source from the manager, discarding any associated mail.
89
115
 
90
- Args:
91
- source_id (str): The ID of the source to delete.
116
+ Parameters
117
+ ----------
118
+ source_id : IDType
119
+ The ID of the source to be removed.
92
120
 
93
- Raises:
94
- LionValueError: If the source does not exist.
121
+ Raises
122
+ ------
123
+ ItemNotFoundError
124
+ If the given source ID is not present.
95
125
  """
96
126
  if source_id not in self.sources:
97
127
  raise ItemNotFoundError(f"Source {source_id} does not exist.")
@@ -99,7 +129,19 @@ class MailManager(Manager):
99
129
  self.mails.pop(source_id)
100
130
 
101
131
  def collect(self, sender: IDType) -> None:
102
- """Collect mail from a specific sender."""
132
+ """
133
+ Collect outbound mail from a single source.
134
+
135
+ Parameters
136
+ ----------
137
+ sender : IDType
138
+ The ID of the sender whose outbound mail is retrieved.
139
+
140
+ Raises
141
+ ------
142
+ ItemNotFoundError
143
+ If the sender is not recognized.
144
+ """
103
145
  if sender not in self.sources:
104
146
  raise ItemNotFoundError(f"Sender source {sender} does not exist.")
105
147
  mailbox: Exchange = (
@@ -120,7 +162,19 @@ class MailManager(Manager):
120
162
  self.mails[mail.recipient][mail.sender].append(mail)
121
163
 
122
164
  def send(self, recipient: IDType) -> None:
123
- """Send mail to a specific recipient."""
165
+ """
166
+ Send any pending mail to a specified recipient.
167
+
168
+ Parameters
169
+ ----------
170
+ recipient : IDType
171
+ The ID of the recipient to which mail should be delivered.
172
+
173
+ Raises
174
+ ------
175
+ ItemNotFoundError
176
+ If the recipient ID is not recognized.
177
+ """
124
178
  if recipient not in self.sources:
125
179
  raise ItemNotFoundError(
126
180
  f"Recipient source {recipient} does not exist."
@@ -139,25 +193,27 @@ class MailManager(Manager):
139
193
  mailbox.include(mail, direction="in")
140
194
 
141
195
  def collect_all(self) -> None:
142
- """Collect mail from all sources."""
196
+ """
197
+ Collect outbound mail from all known sources.
198
+ """
143
199
  for source in self.sources:
144
200
  self.collect(sender=source.id)
145
201
 
146
202
  def send_all(self) -> None:
147
- """Send mail to all recipients."""
203
+ """
204
+ Send mail to all known recipients who have pending items.
205
+ """
148
206
  for source in self.sources:
149
207
  self.send(recipient=source.id)
150
208
 
151
209
  async def execute(self, refresh_time: int = 1) -> None:
152
210
  """
153
- Execute mail collection and sending process asynchronously.
154
-
155
- This method runs in a loop, collecting and sending mail at
156
- regular intervals.
211
+ Continuously collect and send mail in an asynchronous loop.
157
212
 
158
- Args:
159
- refresh_time (int, optional): The time to wait between
160
- each cycle in seconds. Defaults to 1.
213
+ Parameters
214
+ ----------
215
+ refresh_time : int, optional
216
+ Delay (in seconds) between each collect/send cycle.
161
217
  """
162
218
  while not self.execute_stop:
163
219
  self.collect_all()
@@ -165,4 +221,4 @@ class MailManager(Manager):
165
221
  await asyncio.sleep(refresh_time)
166
222
 
167
223
 
168
- # File: lion_core/communication/mail_manager.py
224
+ # File: lion_core/communication/manager.py