mail-swarms 1.3.2__py3-none-any.whl → 1.3.3__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.
- mail/api.py +5 -0
- mail/cli.py +14 -0
- mail/config/server.py +7 -0
- mail/core/runtime.py +38 -0
- mail/factories/action.py +7 -0
- mail/factories/base.py +54 -32
- mail/factories/supervisor.py +7 -0
- mail/server.py +7 -1
- {mail_swarms-1.3.2.dist-info → mail_swarms-1.3.3.dist-info}/METADATA +3 -3
- {mail_swarms-1.3.2.dist-info → mail_swarms-1.3.3.dist-info}/RECORD +15 -15
- {mail_swarms-1.3.2.dist-info → mail_swarms-1.3.3.dist-info}/licenses/THIRD_PARTY_NOTICES.md +3 -3
- {mail_swarms-1.3.2.dist-info → mail_swarms-1.3.3.dist-info}/WHEEL +0 -0
- {mail_swarms-1.3.2.dist-info → mail_swarms-1.3.3.dist-info}/entry_points.txt +0 -0
- {mail_swarms-1.3.2.dist-info → mail_swarms-1.3.3.dist-info}/licenses/LICENSE +0 -0
- {mail_swarms-1.3.2.dist-info → mail_swarms-1.3.3.dist-info}/licenses/NOTICE +0 -0
mail/api.py
CHANGED
|
@@ -748,6 +748,7 @@ class MAILSwarm:
|
|
|
748
748
|
description: str = "",
|
|
749
749
|
keywords: list[str] = [],
|
|
750
750
|
enable_db_agent_histories: bool = False,
|
|
751
|
+
print_llm_streams: bool = True,
|
|
751
752
|
) -> None:
|
|
752
753
|
self.name = name
|
|
753
754
|
self.version = version
|
|
@@ -763,6 +764,7 @@ class MAILSwarm:
|
|
|
763
764
|
self.task_message_limit = task_message_limit
|
|
764
765
|
self.description = description
|
|
765
766
|
self.keywords = keywords
|
|
767
|
+
self.print_llm_streams = print_llm_streams
|
|
766
768
|
self.adjacency_matrix, self.agent_names = self._build_adjacency_matrix()
|
|
767
769
|
self.supervisors = [agent for agent in agents if agent.can_complete_tasks]
|
|
768
770
|
self._agent_cores = {agent.name: agent.to_core() for agent in agents}
|
|
@@ -778,6 +780,7 @@ class MAILSwarm:
|
|
|
778
780
|
breakpoint_tools=breakpoint_tools,
|
|
779
781
|
exclude_tools=exclude_tools,
|
|
780
782
|
enable_db_agent_histories=enable_db_agent_histories,
|
|
783
|
+
print_llm_streams=print_llm_streams,
|
|
781
784
|
)
|
|
782
785
|
self._validate()
|
|
783
786
|
|
|
@@ -1543,6 +1546,7 @@ class MAILSwarmTemplate:
|
|
|
1543
1546
|
user_role: Literal["admin", "agent", "user"] = "user",
|
|
1544
1547
|
base_url: str = "http://localhost:8000",
|
|
1545
1548
|
registry_file: str | None = None,
|
|
1549
|
+
print_llm_streams: bool = True,
|
|
1546
1550
|
) -> MAILSwarm:
|
|
1547
1551
|
"""
|
|
1548
1552
|
Instantiate a MAILSwarm from a MAILSwarmTemplate.
|
|
@@ -1638,6 +1642,7 @@ class MAILSwarmTemplate:
|
|
|
1638
1642
|
description=self.description,
|
|
1639
1643
|
keywords=self.keywords,
|
|
1640
1644
|
enable_db_agent_histories=self.enable_db_agent_histories,
|
|
1645
|
+
print_llm_streams=print_llm_streams,
|
|
1641
1646
|
)
|
|
1642
1647
|
|
|
1643
1648
|
def get_subswarm(
|
mail/cli.py
CHANGED
|
@@ -68,10 +68,18 @@ def _run_server_with_args(args: argparse.Namespace) -> None:
|
|
|
68
68
|
if args.swarm_registry is not None:
|
|
69
69
|
swarm_overrides["registry_file"] = args.swarm_registry
|
|
70
70
|
|
|
71
|
+
settings_overrides: dict[str, object] = {}
|
|
72
|
+
if args.print_llm_streams is not None:
|
|
73
|
+
settings_overrides["print_llm_streams"] = args.print_llm_streams
|
|
74
|
+
|
|
71
75
|
if swarm_overrides:
|
|
72
76
|
server_overrides["swarm"] = base_config.swarm.model_copy(
|
|
73
77
|
update=swarm_overrides
|
|
74
78
|
)
|
|
79
|
+
if settings_overrides:
|
|
80
|
+
server_overrides["settings"] = base_config.settings.model_copy(
|
|
81
|
+
update=settings_overrides
|
|
82
|
+
)
|
|
75
83
|
|
|
76
84
|
effective_config = (
|
|
77
85
|
base_config.model_copy(update=server_overrides)
|
|
@@ -345,6 +353,12 @@ def main() -> None:
|
|
|
345
353
|
required=False,
|
|
346
354
|
help="registry file of the swarm",
|
|
347
355
|
)
|
|
356
|
+
server_parser.add_argument(
|
|
357
|
+
"--print-llm-streams",
|
|
358
|
+
type=_str_to_bool,
|
|
359
|
+
required=False,
|
|
360
|
+
help="whether to print LLM reasoning/response streams to the console (true/false)",
|
|
361
|
+
)
|
|
348
362
|
|
|
349
363
|
# command `client`
|
|
350
364
|
client_parser = subparsers.add_parser("client", help="run the MAIL client")
|
mail/config/server.py
CHANGED
|
@@ -68,6 +68,7 @@ def _load_defaults_from_toml() -> tuple[dict[str, Any], dict[str, Any], dict[str
|
|
|
68
68
|
}
|
|
69
69
|
settings_defaults: dict[str, Any] = {
|
|
70
70
|
"task_message_limit": 15,
|
|
71
|
+
"print_llm_streams": True,
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
if tomllib is None:
|
|
@@ -118,6 +119,9 @@ def _load_defaults_from_toml() -> tuple[dict[str, Any], dict[str, Any], dict[str
|
|
|
118
119
|
"task_message_limit": settings_section.get(
|
|
119
120
|
"task_message_limit", settings_defaults["task_message_limit"]
|
|
120
121
|
),
|
|
122
|
+
"print_llm_streams": settings_section.get(
|
|
123
|
+
"print_llm_streams", settings_defaults["print_llm_streams"]
|
|
124
|
+
),
|
|
121
125
|
}
|
|
122
126
|
|
|
123
127
|
logger.info(
|
|
@@ -153,6 +157,9 @@ class SettingsConfig(BaseModel):
|
|
|
153
157
|
task_message_limit: int = Field(
|
|
154
158
|
default_factory=lambda: _settings_defaults()["task_message_limit"]
|
|
155
159
|
)
|
|
160
|
+
print_llm_streams: bool = Field(
|
|
161
|
+
default_factory=lambda: _settings_defaults()["print_llm_streams"]
|
|
162
|
+
)
|
|
156
163
|
|
|
157
164
|
|
|
158
165
|
class ServerConfig(BaseModel):
|
mail/core/runtime.py
CHANGED
|
@@ -94,6 +94,7 @@ class MAILRuntime:
|
|
|
94
94
|
breakpoint_tools: list[str] | None = None,
|
|
95
95
|
exclude_tools: list[str] | None = None,
|
|
96
96
|
enable_db_agent_histories: bool = False,
|
|
97
|
+
print_llm_streams: bool = True,
|
|
97
98
|
):
|
|
98
99
|
# Use a priority queue with a deterministic tiebreaker to avoid comparing dicts
|
|
99
100
|
# Structure: (priority, seq, message)
|
|
@@ -142,6 +143,7 @@ class MAILRuntime:
|
|
|
142
143
|
self.manual_return_events: dict[str, asyncio.Event] = defaultdict(asyncio.Event)
|
|
143
144
|
self.manual_return_messages: dict[str, MAILMessage | None] = defaultdict(None)
|
|
144
145
|
self.exclude_tools = list(exclude_tools or [])
|
|
146
|
+
self.print_llm_streams = print_llm_streams
|
|
145
147
|
self.response_messages: dict[str, MAILMessage] = {}
|
|
146
148
|
self.last_breakpoint_caller: dict[str, str] = {}
|
|
147
149
|
self.last_breakpoint_tool_calls: dict[str, list[AgentToolCall]] = {}
|
|
@@ -151,6 +153,42 @@ class MAILRuntime:
|
|
|
151
153
|
self.outstanding_requests: dict[str, dict[str, int]] = defaultdict(
|
|
152
154
|
lambda: defaultdict(int)
|
|
153
155
|
)
|
|
156
|
+
self._apply_llm_stream_settings()
|
|
157
|
+
|
|
158
|
+
def _apply_llm_stream_settings(self) -> None:
|
|
159
|
+
"""
|
|
160
|
+
Best-effort propagation of LLM stream print settings to agent functions.
|
|
161
|
+
"""
|
|
162
|
+
seen: set[int] = set()
|
|
163
|
+
for agent in self.agents.values():
|
|
164
|
+
self._set_llm_stream_setting(agent.function, seen)
|
|
165
|
+
|
|
166
|
+
def _set_llm_stream_setting(
|
|
167
|
+
self, fn: Any, seen: set[int], value: bool | None = None
|
|
168
|
+
) -> None:
|
|
169
|
+
"""
|
|
170
|
+
Recursively apply print_llm_streams to known agent function wrappers.
|
|
171
|
+
"""
|
|
172
|
+
if fn is None:
|
|
173
|
+
return
|
|
174
|
+
fn_id = id(fn)
|
|
175
|
+
if fn_id in seen:
|
|
176
|
+
return
|
|
177
|
+
seen.add(fn_id)
|
|
178
|
+
stream_value = self.print_llm_streams if value is None else value
|
|
179
|
+
|
|
180
|
+
if hasattr(fn, "print_llm_streams"):
|
|
181
|
+
try:
|
|
182
|
+
setattr(fn, "print_llm_streams", stream_value)
|
|
183
|
+
except Exception: # pragma: no cover - defensive
|
|
184
|
+
logger.debug(
|
|
185
|
+
f"{self._log_prelude()} unable to update print_llm_streams on {fn}"
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
for attr in ("supervisor_fn", "action_agent_fn", "_mail_agent"):
|
|
189
|
+
inner = getattr(fn, attr, None)
|
|
190
|
+
if inner is not None:
|
|
191
|
+
self._set_llm_stream_setting(inner, seen, stream_value)
|
|
154
192
|
|
|
155
193
|
def _log_prelude(self) -> str:
|
|
156
194
|
"""
|
mail/factories/action.py
CHANGED
|
@@ -43,6 +43,7 @@ def action_agent_factory(
|
|
|
43
43
|
memory: bool = True,
|
|
44
44
|
use_proxy: bool = True,
|
|
45
45
|
_debug_include_mail_tools: bool = True,
|
|
46
|
+
print_llm_streams: bool = True,
|
|
46
47
|
) -> AgentFunction:
|
|
47
48
|
warnings.warn(
|
|
48
49
|
"`mail.factories.action:action_agent_factory` is deprecated and will be removed in a future version. "
|
|
@@ -69,6 +70,7 @@ def action_agent_factory(
|
|
|
69
70
|
memory=memory,
|
|
70
71
|
use_proxy=use_proxy,
|
|
71
72
|
_debug_include_mail_tools=_debug_include_mail_tools,
|
|
73
|
+
print_llm_streams=print_llm_streams,
|
|
72
74
|
)
|
|
73
75
|
|
|
74
76
|
async def run(
|
|
@@ -80,6 +82,8 @@ def action_agent_factory(
|
|
|
80
82
|
"""
|
|
81
83
|
return await litellm_action_agent(messages, tool_choice)
|
|
82
84
|
|
|
85
|
+
run._mail_agent = litellm_action_agent # type: ignore[attr-defined]
|
|
86
|
+
|
|
83
87
|
return run
|
|
84
88
|
|
|
85
89
|
|
|
@@ -159,6 +163,7 @@ class LiteLLMActionAgentFunction(ActionAgentFunction):
|
|
|
159
163
|
use_proxy: bool = True,
|
|
160
164
|
_debug_include_mail_tools: bool = True,
|
|
161
165
|
stream_tokens: bool = False,
|
|
166
|
+
print_llm_streams: bool = True,
|
|
162
167
|
default_tool_choice: str | dict[str, str] | None = None,
|
|
163
168
|
) -> None:
|
|
164
169
|
super().__init__(
|
|
@@ -181,6 +186,7 @@ class LiteLLMActionAgentFunction(ActionAgentFunction):
|
|
|
181
186
|
self.use_proxy = use_proxy
|
|
182
187
|
self._debug_include_mail_tools = _debug_include_mail_tools
|
|
183
188
|
self.stream_tokens = stream_tokens
|
|
189
|
+
self.print_llm_streams = print_llm_streams
|
|
184
190
|
self.default_tool_choice = default_tool_choice
|
|
185
191
|
self.action_agent_fn = LiteLLMAgentFunction(
|
|
186
192
|
llm=self.llm,
|
|
@@ -201,6 +207,7 @@ class LiteLLMActionAgentFunction(ActionAgentFunction):
|
|
|
201
207
|
exclude_tools=self.exclude_tools,
|
|
202
208
|
_debug_include_mail_tools=self._debug_include_mail_tools,
|
|
203
209
|
stream_tokens=self.stream_tokens,
|
|
210
|
+
print_llm_streams=self.print_llm_streams,
|
|
204
211
|
)
|
|
205
212
|
|
|
206
213
|
def __call__(
|
mail/factories/base.py
CHANGED
|
@@ -57,6 +57,7 @@ def base_agent_factory(
|
|
|
57
57
|
memory: bool = True,
|
|
58
58
|
use_proxy: bool = True,
|
|
59
59
|
stream_tokens: bool = False,
|
|
60
|
+
print_llm_streams: bool = True,
|
|
60
61
|
_debug_include_mail_tools: bool = True,
|
|
61
62
|
default_tool_choice: str | dict[str, str] | None = None,
|
|
62
63
|
) -> AgentFunction:
|
|
@@ -85,6 +86,7 @@ def base_agent_factory(
|
|
|
85
86
|
memory=memory,
|
|
86
87
|
use_proxy=use_proxy,
|
|
87
88
|
stream_tokens=stream_tokens,
|
|
89
|
+
print_llm_streams=print_llm_streams,
|
|
88
90
|
_debug_include_mail_tools=_debug_include_mail_tools,
|
|
89
91
|
default_tool_choice=default_tool_choice,
|
|
90
92
|
)
|
|
@@ -102,6 +104,8 @@ def base_agent_factory(
|
|
|
102
104
|
tool_choice=tool_choice,
|
|
103
105
|
)
|
|
104
106
|
|
|
107
|
+
run._mail_agent = litellm_agent # type: ignore[attr-defined]
|
|
108
|
+
|
|
105
109
|
return run
|
|
106
110
|
|
|
107
111
|
|
|
@@ -168,6 +172,7 @@ class LiteLLMAgentFunction(MAILAgentFunction):
|
|
|
168
172
|
memory: bool = True,
|
|
169
173
|
use_proxy: bool = True,
|
|
170
174
|
stream_tokens: bool = False,
|
|
175
|
+
print_llm_streams: bool = True,
|
|
171
176
|
_debug_include_mail_tools: bool = True,
|
|
172
177
|
default_tool_choice: str | dict[str, str] | None = None,
|
|
173
178
|
) -> None:
|
|
@@ -219,6 +224,7 @@ class LiteLLMAgentFunction(MAILAgentFunction):
|
|
|
219
224
|
self.memory = memory
|
|
220
225
|
self.use_proxy = use_proxy
|
|
221
226
|
self.stream_tokens = stream_tokens
|
|
227
|
+
self.print_llm_streams = print_llm_streams
|
|
222
228
|
self._debug_include_mail_tools = _debug_include_mail_tools
|
|
223
229
|
self.default_tool_choice = default_tool_choice
|
|
224
230
|
|
|
@@ -903,32 +909,37 @@ class LiteLLMAgentFunction(MAILAgentFunction):
|
|
|
903
909
|
|
|
904
910
|
if block_type == "thinking":
|
|
905
911
|
if not is_reasoning:
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
912
|
+
if self.print_llm_streams:
|
|
913
|
+
rich.print(
|
|
914
|
+
f"\n\n[bold green]{'=' * 21} REASONING {'=' * 21}[/bold green]\n\n"
|
|
915
|
+
)
|
|
909
916
|
is_reasoning = True
|
|
910
917
|
|
|
911
918
|
elif block_type == "redacted_thinking":
|
|
912
919
|
# Redacted thinking blocks contain encrypted content
|
|
913
920
|
if not is_reasoning:
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
921
|
+
if self.print_llm_streams:
|
|
922
|
+
rich.print(
|
|
923
|
+
f"\n\n[bold green]{'=' * 21} REASONING {'=' * 21}[/bold green]\n\n"
|
|
924
|
+
)
|
|
917
925
|
is_reasoning = True
|
|
918
|
-
|
|
926
|
+
if self.print_llm_streams:
|
|
927
|
+
rich.print("[redacted thinking]", flush=True)
|
|
919
928
|
|
|
920
929
|
elif block_type == "server_tool_use":
|
|
921
930
|
if not is_searching:
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
931
|
+
if self.print_llm_streams:
|
|
932
|
+
rich.print(
|
|
933
|
+
f"\n\n[bold yellow]{'=' * 21} WEB SEARCH {'=' * 21}[/bold yellow]\n\n"
|
|
934
|
+
)
|
|
925
935
|
is_searching = True
|
|
926
936
|
|
|
927
937
|
elif block_type == "text":
|
|
928
938
|
if not is_response:
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
939
|
+
if self.print_llm_streams:
|
|
940
|
+
rich.print(
|
|
941
|
+
f"\n\n[bold blue]{'=' * 21} RESPONSE {'=' * 21}[/bold blue]\n\n"
|
|
942
|
+
)
|
|
932
943
|
is_response = True
|
|
933
944
|
|
|
934
945
|
elif event_type == "content_block_delta":
|
|
@@ -938,10 +949,12 @@ class LiteLLMAgentFunction(MAILAgentFunction):
|
|
|
938
949
|
|
|
939
950
|
if delta_type == "thinking_delta":
|
|
940
951
|
assert isinstance(delta, ThinkingDelta)
|
|
941
|
-
|
|
952
|
+
if self.print_llm_streams:
|
|
953
|
+
print(delta.thinking, end="", flush=True)
|
|
942
954
|
elif delta_type == "text_delta":
|
|
943
955
|
assert isinstance(delta, TextDelta)
|
|
944
|
-
|
|
956
|
+
if self.print_llm_streams:
|
|
957
|
+
print(delta.text, end="", flush=True)
|
|
945
958
|
|
|
946
959
|
# Get the final message with full content
|
|
947
960
|
final_message = await stream.get_final_message()
|
|
@@ -1128,18 +1141,22 @@ class LiteLLMAgentFunction(MAILAgentFunction):
|
|
|
1128
1141
|
delta = chunk.choices[0].delta
|
|
1129
1142
|
if getattr(delta, "reasoning_content", None) is not None:
|
|
1130
1143
|
if not is_reasoning:
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1144
|
+
if self.print_llm_streams:
|
|
1145
|
+
rich.print(
|
|
1146
|
+
f"\n\n[bold green]{'=' * 21} REASONING {'=' * 21}[/bold green]\n\n"
|
|
1147
|
+
)
|
|
1134
1148
|
is_reasoning = True
|
|
1135
|
-
|
|
1149
|
+
if self.print_llm_streams:
|
|
1150
|
+
rich.print(delta.reasoning_content, end="", flush=True)
|
|
1136
1151
|
elif getattr(delta, "content", None) is not None:
|
|
1137
1152
|
if not is_response:
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1153
|
+
if self.print_llm_streams:
|
|
1154
|
+
rich.print(
|
|
1155
|
+
f"\n\n[bold blue]{'=' * 21} RESPONSE {'=' * 21}[/bold blue]\n\n"
|
|
1156
|
+
)
|
|
1141
1157
|
is_response = True
|
|
1142
|
-
|
|
1158
|
+
if self.print_llm_streams:
|
|
1159
|
+
rich.print(delta.content, end="", flush=True)
|
|
1143
1160
|
chunks.append(chunk)
|
|
1144
1161
|
|
|
1145
1162
|
final_completion = litellm.stream_chunk_builder(chunks, messages=messages)
|
|
@@ -1468,17 +1485,20 @@ class LiteLLMAgentFunction(MAILAgentFunction):
|
|
|
1468
1485
|
async for event in stream:
|
|
1469
1486
|
match event.type:
|
|
1470
1487
|
case "response.created":
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1488
|
+
if self.print_llm_streams:
|
|
1489
|
+
rich.print(
|
|
1490
|
+
f"\n\n[bold green]{'=' * 21} REASONING {'=' * 21}[/bold green]\n\n"
|
|
1491
|
+
)
|
|
1474
1492
|
case "response.reasoning_summary_text.delta":
|
|
1475
1493
|
# Stream reasoning text and accumulate for mapping
|
|
1476
|
-
|
|
1494
|
+
if self.print_llm_streams:
|
|
1495
|
+
rich.print(event.delta, end="", flush=True)
|
|
1477
1496
|
current_reasoning_text.append(event.delta)
|
|
1478
1497
|
|
|
1479
1498
|
case "response.reasoning_summary_part.done":
|
|
1480
1499
|
# Reasoning part complete - finalize the block
|
|
1481
|
-
|
|
1500
|
+
if self.print_llm_streams:
|
|
1501
|
+
rich.print("\n\n")
|
|
1482
1502
|
if current_reasoning_text:
|
|
1483
1503
|
pending_reasoning_parts.append("".join(current_reasoning_text))
|
|
1484
1504
|
current_reasoning_text = []
|
|
@@ -1512,12 +1532,14 @@ class LiteLLMAgentFunction(MAILAgentFunction):
|
|
|
1512
1532
|
pending_reasoning_parts = []
|
|
1513
1533
|
|
|
1514
1534
|
if item_type == "message":
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1535
|
+
if self.print_llm_streams:
|
|
1536
|
+
rich.print(
|
|
1537
|
+
f"\n\n[bold blue]{'=' * 21} RESPONSE {'=' * 21}[/bold blue]\n\n"
|
|
1538
|
+
)
|
|
1518
1539
|
|
|
1519
1540
|
case "response.output_text.delta":
|
|
1520
|
-
|
|
1541
|
+
if self.print_llm_streams:
|
|
1542
|
+
rich.print(event.delta, end="", flush=True)
|
|
1521
1543
|
|
|
1522
1544
|
case "response.completed":
|
|
1523
1545
|
# Defensive: flush any remaining reasoning text
|
mail/factories/supervisor.py
CHANGED
|
@@ -46,6 +46,7 @@ def supervisor_factory(
|
|
|
46
46
|
memory: bool = True,
|
|
47
47
|
use_proxy: bool = True,
|
|
48
48
|
stream_tokens: bool = False,
|
|
49
|
+
print_llm_streams: bool = True,
|
|
49
50
|
default_tool_choice: str | dict[str, str] | None = None,
|
|
50
51
|
) -> AgentFunction:
|
|
51
52
|
"""
|
|
@@ -76,6 +77,7 @@ def supervisor_factory(
|
|
|
76
77
|
tool_format=tool_format,
|
|
77
78
|
exclude_tools=exclude_tools,
|
|
78
79
|
stream_tokens=stream_tokens,
|
|
80
|
+
print_llm_streams=print_llm_streams,
|
|
79
81
|
default_tool_choice=default_tool_choice,
|
|
80
82
|
)
|
|
81
83
|
|
|
@@ -88,6 +90,8 @@ def supervisor_factory(
|
|
|
88
90
|
"""
|
|
89
91
|
return await litellm_supervisor(messages, tool_choice)
|
|
90
92
|
|
|
93
|
+
run._mail_agent = litellm_supervisor # type: ignore[attr-defined]
|
|
94
|
+
|
|
91
95
|
return run
|
|
92
96
|
|
|
93
97
|
|
|
@@ -183,6 +187,7 @@ class LiteLLMSupervisorFunction(SupervisorFunction):
|
|
|
183
187
|
use_proxy: bool = True,
|
|
184
188
|
_debug_include_mail_tools: bool = True,
|
|
185
189
|
stream_tokens: bool = False,
|
|
190
|
+
print_llm_streams: bool = True,
|
|
186
191
|
default_tool_choice: str | dict[str, str] | None = None,
|
|
187
192
|
) -> None:
|
|
188
193
|
super().__init__(
|
|
@@ -205,6 +210,7 @@ class LiteLLMSupervisorFunction(SupervisorFunction):
|
|
|
205
210
|
self.use_proxy = use_proxy
|
|
206
211
|
self._debug_include_mail_tools = _debug_include_mail_tools
|
|
207
212
|
self.stream_tokens = stream_tokens
|
|
213
|
+
self.print_llm_streams = print_llm_streams
|
|
208
214
|
self.default_tool_choice = default_tool_choice
|
|
209
215
|
self.supervisor_fn = LiteLLMAgentFunction(
|
|
210
216
|
name=self.name,
|
|
@@ -224,6 +230,7 @@ class LiteLLMSupervisorFunction(SupervisorFunction):
|
|
|
224
230
|
use_proxy=self.use_proxy,
|
|
225
231
|
_debug_include_mail_tools=self._debug_include_mail_tools,
|
|
226
232
|
stream_tokens=self.stream_tokens,
|
|
233
|
+
print_llm_streams=self.print_llm_streams,
|
|
227
234
|
default_tool_choice=self.default_tool_choice,
|
|
228
235
|
)
|
|
229
236
|
|
mail/server.py
CHANGED
|
@@ -98,6 +98,7 @@ async def _server_startup(app: FastAPI) -> None:
|
|
|
98
98
|
|
|
99
99
|
# set defaults
|
|
100
100
|
app.state.debug = cfg.debug
|
|
101
|
+
app.state.settings = cfg.settings
|
|
101
102
|
|
|
102
103
|
# swarm stuff
|
|
103
104
|
app.state.persistent_swarm = ps
|
|
@@ -333,6 +334,7 @@ async def get_or_create_mail_instance(
|
|
|
333
334
|
user_role=role,
|
|
334
335
|
base_url=app.state.local_base_url,
|
|
335
336
|
registry_file=app.state.swarm_registry.persistence_file,
|
|
337
|
+
print_llm_streams=app.state.settings.print_llm_streams,
|
|
336
338
|
)
|
|
337
339
|
mail_instances[id] = mail_instance
|
|
338
340
|
|
|
@@ -1553,6 +1555,7 @@ def run_server_with_template(
|
|
|
1553
1555
|
port: int = 8000,
|
|
1554
1556
|
host: str = "0.0.0.0",
|
|
1555
1557
|
task_message_limit: int | None = None,
|
|
1558
|
+
print_llm_streams: bool = True,
|
|
1556
1559
|
) -> None:
|
|
1557
1560
|
"""Run MAIL server with a pre-configured swarm template.
|
|
1558
1561
|
|
|
@@ -1579,7 +1582,10 @@ def run_server_with_template(
|
|
|
1579
1582
|
),
|
|
1580
1583
|
settings=SettingsConfig(
|
|
1581
1584
|
# Use large sentinel for "unlimited" - avoids changing type throughout codebase
|
|
1582
|
-
task_message_limit=task_message_limit
|
|
1585
|
+
task_message_limit=task_message_limit
|
|
1586
|
+
if task_message_limit is not None
|
|
1587
|
+
else 999999,
|
|
1588
|
+
print_llm_streams=print_llm_streams,
|
|
1583
1589
|
),
|
|
1584
1590
|
)
|
|
1585
1591
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mail-swarms
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.3
|
|
4
4
|
Summary: Multi-Agent Interface Layer reference implementation
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -121,7 +121,7 @@ The runtime processes MAIL messages **asynchronously**, tracks per-task state, a
|
|
|
121
121
|
### Installation
|
|
122
122
|
```bash
|
|
123
123
|
# Clone and enter the repository
|
|
124
|
-
git clone https://github.com/charonlabs/mail --branch v1.3.
|
|
124
|
+
git clone https://github.com/charonlabs/mail --branch v1.3.3
|
|
125
125
|
cd mail
|
|
126
126
|
|
|
127
127
|
# Install dependencies (preferred)
|
|
@@ -150,7 +150,7 @@ export ANTHROPIC_API_KEY=sk-your-anthropic-key
|
|
|
150
150
|
export DATABASE_URL=postgresql://...
|
|
151
151
|
```
|
|
152
152
|
|
|
153
|
-
Defaults for host, port, swarm metadata, and client behaviour are loaded from [`mail.toml`](mail.toml). The `[server.settings]` table exposes `task_message_limit`, which bounds how many messages the runtime will process per task when `run_continuous` is active (default `15`). Override the file or point `MAIL_CONFIG_PATH` at an alternate TOML to adjust these values per environment. Use
|
|
153
|
+
Defaults for host, port, swarm metadata, and client behaviour are loaded from [`mail.toml`](mail.toml). The `[server.settings]` table exposes `task_message_limit`, which bounds how many messages the runtime will process per task when `run_continuous` is active (default `15`), and `print_llm_streams` (default `true`), which controls whether runtime-managed agents print LLM reasoning/response stream chunks to server stdout. Set `print_llm_streams=false` (or pass `mail server --print-llm-streams false`) for quieter server logs; task/event SSE streaming is unaffected. Override the file or point `MAIL_CONFIG_PATH` at an alternate TOML to adjust these values per environment. Use CLI flags such as `--swarm-name`, `--swarm-source`, `--swarm-registry`, and `--print-llm-streams true|false` (or edit `mail.toml`) to override these at launch; `mail server` exports `SWARM_NAME`, `SWARM_SOURCE`, `SWARM_REGISTRY_FILE`, and `BASE_URL` for downstream tools but does not read them as config overrides.
|
|
154
154
|
|
|
155
155
|
MAIL will create the parent directory for `SWARM_REGISTRY_FILE` on startup if it is missing, so you can rely on the default `registries/` path without committing the folder.
|
|
156
156
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
mail/__init__.py,sha256=Rj44tvgXvcEjx_-l2aBilzCmrCdzLPXMREP-KNj0uZk,600
|
|
2
|
-
mail/api.py,sha256=
|
|
3
|
-
mail/cli.py,sha256=
|
|
2
|
+
mail/api.py,sha256=_H5eng3Rbcocmy7dA36UWejET5xakF0AhGqEZriMTm8,74205
|
|
3
|
+
mail/cli.py,sha256=dvNYsrv8_tU_YXkwAo27sPyPmnGTKYz5tTBqE3vvsDE,13221
|
|
4
4
|
mail/client.py,sha256=igbcjGlz2Z-9LvGHf_qyACfVdeMVLnFuMXaMn740daY,56909
|
|
5
|
-
mail/server.py,sha256=
|
|
5
|
+
mail/server.py,sha256=xc5OUUsLTTBTXxZMNqnWPIEASY9CFDz0ArR--razDY4,56185
|
|
6
6
|
mail/summarizer.py,sha256=clG4d1mcSHmGagvKva_P3trw46sd2XJPJ7lJ_o-e0qM,7468
|
|
7
7
|
mail/url_scheme.py,sha256=RRqXuIzmQAq_depvn1-bht9yEnTgxPy8R2s_8FbDF9M,1229
|
|
8
8
|
mail/config/__init__.py,sha256=bP2yX8fTyRm5Iv362uiQBf18rsh9H7coNoPtbpZJAeU,153
|
|
9
9
|
mail/config/client.py,sha256=aFuCqqvGnZQsDDqAi6aaWIjU8o3OPc41PkDAqYtnPZs,2581
|
|
10
|
-
mail/config/server.py,sha256=
|
|
10
|
+
mail/config/server.py,sha256=w5P_Cf9WbG4nzoMPQC4hGhq8yeRURgedCbycgHf9tMo,6151
|
|
11
11
|
mail/core/__init__.py,sha256=v85xchqjsprvsOUUyf3lxdD0_89Il3TEQE2sMyJyk0I,1833
|
|
12
12
|
mail/core/actions.py,sha256=ZwXfceB-6aNrCEjcwOreUTxku2nTuh22_pemqY-cDWE,2249
|
|
13
13
|
mail/core/agents.py,sha256=hiCugGB8t3XicdiPmNWBd9Yfn1FqBJoAYNWao9Joy74,2084
|
|
14
14
|
mail/core/message.py,sha256=q70d0b5GB8YHMTCMrdvWhyTRDlazfydbc-5sktBqeUY,10812
|
|
15
|
-
mail/core/runtime.py,sha256=
|
|
15
|
+
mail/core/runtime.py,sha256=7w_XtYf1zt38x7YppZmfnOZYR-mOzfvSE8YivYy-L-M,156171
|
|
16
16
|
mail/core/tasks.py,sha256=CUtc8SQIoAuvBhqSl6uPPCI-bVWbG538EOH6optDzyw,10048
|
|
17
17
|
mail/core/tools.py,sha256=DMrUme5gBgoMbR2Tv3aE_pTAumHNAKRTqW2ganPI1uc,42993
|
|
18
18
|
mail/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -95,9 +95,9 @@ mail/examples/weather_dummy/agent.py,sha256=ehoMg_0fXZcRYJ9VfsSwpKwxyG4mSvjBlyHU
|
|
|
95
95
|
mail/examples/weather_dummy/prompts.py,sha256=sCAeGktBbSf_RGWkC1CKHLlLidura86hyOE7tF5UPRY,1586
|
|
96
96
|
mail/examples/weather_dummy/types.py,sha256=rWNU2Fz_1LJdm54iyzBgy3H_Mx6IzIsdpA_ofTO56OM,164
|
|
97
97
|
mail/factories/__init__.py,sha256=qcUcc_ZHQzpk4pgdydZxhmv3HQLumoazGOjm5tEn6Zg,569
|
|
98
|
-
mail/factories/action.py,sha256=
|
|
99
|
-
mail/factories/base.py,sha256=
|
|
100
|
-
mail/factories/supervisor.py,sha256=
|
|
98
|
+
mail/factories/action.py,sha256=5gB6vsW8XY07jAUV-n8WQA6dBcCeQPPtNhALk5zfnJs,7598
|
|
99
|
+
mail/factories/base.py,sha256=lzUk9f-oj0sVYpmz7dtU02LVosfD229kAkVPDWs-nPM,62663
|
|
100
|
+
mail/factories/supervisor.py,sha256=OuteeG97jfpStyLKaapwPzhqWqCg6IFuj1AAZZOX57w,8233
|
|
101
101
|
mail/net/__init__.py,sha256=s6obszeuL0nMHsfR9zROMKi1aRDAwtf1XXB5z6jqtJE,133
|
|
102
102
|
mail/net/registry.py,sha256=rP-_E53JC9iyt83SzDKlRHHR6pSmBm38sjx9XiCxnwQ,27894
|
|
103
103
|
mail/net/router.py,sha256=gc2wB_hgWL8WJmZryZQPT0tslbO9Dtct7IpFCa2RKGE,28729
|
|
@@ -128,10 +128,10 @@ mail/utils/serialize.py,sha256=S-nqIwodDXZrsgHDrBg2ndur4fMnmFXKRf8kKl_Z0yc,9753
|
|
|
128
128
|
mail/utils/store.py,sha256=VJ9GoHjFWcWeDWoOwS7Ujh-8rHqxvMQtGJDg2XA1ekI,1495
|
|
129
129
|
mail/utils/string_builder.py,sha256=eAlGUzfKqxgfsAIJfpSoFlZ2hqP2PvSVnNE5FxjprII,4516
|
|
130
130
|
mail/utils/version.py,sha256=nROAkYcqiff1U-58EPkCaGQ-OrSogu1IRnq7cPrU1EA,605
|
|
131
|
-
mail_swarms-1.3.
|
|
132
|
-
mail_swarms-1.3.
|
|
133
|
-
mail_swarms-1.3.
|
|
134
|
-
mail_swarms-1.3.
|
|
135
|
-
mail_swarms-1.3.
|
|
136
|
-
mail_swarms-1.3.
|
|
137
|
-
mail_swarms-1.3.
|
|
131
|
+
mail_swarms-1.3.3.dist-info/METADATA,sha256=zZAvUEIaJSmlVquixWw1geKNVrGW7MS2KXSfpXGNqeU,14098
|
|
132
|
+
mail_swarms-1.3.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
133
|
+
mail_swarms-1.3.3.dist-info/entry_points.txt,sha256=vgOgm2rAG5-LvkCG6vHYyrL6RNkoOvTLM0zLHAXUEgY,39
|
|
134
|
+
mail_swarms-1.3.3.dist-info/licenses/LICENSE,sha256=9g90ibSFxcR9tnPkhYkH7-Bpa9oXBfiHD-spD1_KtZ4,10259
|
|
135
|
+
mail_swarms-1.3.3.dist-info/licenses/NOTICE,sha256=yp2jlUSYD9Yn-jrK_hyiE3wJzzQe2xbS92a2dLceU7A,327
|
|
136
|
+
mail_swarms-1.3.3.dist-info/licenses/THIRD_PARTY_NOTICES.md,sha256=WwhEdrdgrU_iVVVo4h9NeFhDqHcQlxC8ejDIYysP5hc,596162
|
|
137
|
+
mail_swarms-1.3.3.dist-info/RECORD,,
|
|
@@ -5124,14 +5124,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
5124
5124
|
SOFTWARE.
|
|
5125
5125
|
```
|
|
5126
5126
|
|
|
5127
|
-
## `mail-swarms==1.3.
|
|
5127
|
+
## `mail-swarms==1.3.3`
|
|
5128
5128
|
Multi-Agent Interface Layer reference implementation
|
|
5129
5129
|
### License field:
|
|
5130
5130
|
Apache-2.0
|
|
5131
5131
|
### Classifiers:
|
|
5132
5132
|
License :: OSI Approved :: Apache Software License
|
|
5133
5133
|
|
|
5134
|
-
### License Text (`mail_swarms-1.3.
|
|
5134
|
+
### License Text (`mail_swarms-1.3.3.dist-info/licenses/LICENSE`)
|
|
5135
5135
|
|
|
5136
5136
|
```text
|
|
5137
5137
|
Apache License
|
|
@@ -5337,7 +5337,7 @@ See the License for the specific language governing permissions and
|
|
|
5337
5337
|
limitations under the License.
|
|
5338
5338
|
```
|
|
5339
5339
|
|
|
5340
|
-
### License Text (`mail_swarms-1.3.
|
|
5340
|
+
### License Text (`mail_swarms-1.3.3.dist-info/licenses/NOTICE`)
|
|
5341
5341
|
|
|
5342
5342
|
```text
|
|
5343
5343
|
MAIL Project
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|