lionagi 0.17.4__py3-none-any.whl → 0.17.6__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 (44) hide show
  1. lionagi/__init__.py +45 -7
  2. lionagi/config.py +26 -0
  3. lionagi/fields/action.py +5 -3
  4. lionagi/libs/file/chunk.py +3 -14
  5. lionagi/libs/file/process.py +10 -92
  6. lionagi/libs/schema/breakdown_pydantic_annotation.py +45 -0
  7. lionagi/ln/_async_call.py +19 -8
  8. lionagi/ln/_hash.py +12 -2
  9. lionagi/ln/_to_list.py +23 -12
  10. lionagi/ln/fuzzy/_fuzzy_match.py +3 -6
  11. lionagi/ln/fuzzy/_fuzzy_validate.py +9 -8
  12. lionagi/ln/fuzzy/_string_similarity.py +11 -5
  13. lionagi/ln/fuzzy/_to_dict.py +19 -19
  14. lionagi/ln/types.py +15 -0
  15. lionagi/operations/operate/operate.py +7 -11
  16. lionagi/operations/parse/parse.py +5 -3
  17. lionagi/protocols/generic/element.py +3 -6
  18. lionagi/protocols/generic/event.py +1 -1
  19. lionagi/protocols/mail/package.py +2 -2
  20. lionagi/protocols/messages/instruction.py +9 -1
  21. lionagi/protocols/operatives/operative.py +4 -3
  22. lionagi/service/broadcaster.py +61 -0
  23. lionagi/service/connections/api_calling.py +22 -145
  24. lionagi/service/connections/mcp/wrapper.py +8 -15
  25. lionagi/service/hooks/__init__.py +2 -10
  26. lionagi/service/hooks/_types.py +1 -0
  27. lionagi/service/hooks/hooked_event.py +142 -0
  28. lionagi/service/imodel.py +2 -2
  29. lionagi/session/branch.py +46 -169
  30. lionagi/session/session.py +1 -44
  31. lionagi/tools/file/reader.py +6 -4
  32. lionagi/utils.py +3 -342
  33. lionagi/version.py +1 -1
  34. {lionagi-0.17.4.dist-info → lionagi-0.17.6.dist-info}/METADATA +4 -4
  35. {lionagi-0.17.4.dist-info → lionagi-0.17.6.dist-info}/RECORD +37 -41
  36. lionagi/libs/file/_utils.py +0 -10
  37. lionagi/libs/file/concat.py +0 -121
  38. lionagi/libs/file/concat_files.py +0 -85
  39. lionagi/libs/file/file_ops.py +0 -118
  40. lionagi/libs/file/save.py +0 -103
  41. lionagi/ln/concurrency/throttle.py +0 -83
  42. lionagi/settings.py +0 -71
  43. {lionagi-0.17.4.dist-info → lionagi-0.17.6.dist-info}/WHEEL +0 -0
  44. {lionagi-0.17.4.dist-info → lionagi-0.17.6.dist-info}/licenses/LICENSE +0 -0
@@ -7,15 +7,7 @@ import json
7
7
  import logging
8
8
  import os
9
9
  from pathlib import Path
10
- from typing import Any, Dict
11
-
12
- try:
13
- from fastmcp import Client as FastMCPClient
14
-
15
- FASTMCP_AVAILABLE = True
16
- except ImportError:
17
- FASTMCP_AVAILABLE = False
18
- FastMCPClient = None
10
+ from typing import Any
19
11
 
20
12
  # Suppress MCP server logging by default
21
13
  logging.getLogger("mcp").setLevel(logging.WARNING)
@@ -88,11 +80,6 @@ class MCPConnectionPool:
88
80
  @classmethod
89
81
  async def get_client(cls, server_config: dict[str, Any]) -> Any:
90
82
  """Get or create a pooled MCP client."""
91
- if not FASTMCP_AVAILABLE:
92
- raise ImportError(
93
- "FastMCP not installed. Run: pip install fastmcp"
94
- )
95
-
96
83
  # Generate unique key for this config
97
84
  if "server" in server_config:
98
85
  # Server reference from .mcp.json
@@ -149,6 +136,13 @@ class MCPConnectionPool:
149
136
  if not any(k in config for k in ["url", "command"]):
150
137
  raise ValueError("Config must have either 'url' or 'command' key")
151
138
 
139
+ try:
140
+ from fastmcp import Client as FastMCPClient
141
+ except ImportError:
142
+ raise ImportError(
143
+ "FastMCP not installed. Run: pip install fastmcp"
144
+ )
145
+
152
146
  # Handle different config formats
153
147
  if "url" in config:
154
148
  # Direct URL connection
@@ -172,7 +166,6 @@ class MCPConnectionPool:
172
166
  # Common environment variables to suppress logging
173
167
  env.setdefault("LOG_LEVEL", "ERROR")
174
168
  env.setdefault("PYTHONWARNINGS", "ignore")
175
- env.setdefault("KHIVEMCP_LOG_LEVEL", "ERROR")
176
169
  # Suppress FastMCP server logs
177
170
  env.setdefault("FASTMCP_QUIET", "true")
178
171
  env.setdefault("MCP_QUIET", "true")
@@ -1,19 +1,10 @@
1
1
  # Copyright (c) 2025, HaiyangLi <quantocean.li at gmail dot com>
2
2
  # SPDX-License-Identifier: Apache-2.0
3
3
 
4
- from lionagi.protocols.types import DataLogger
5
-
6
4
  from ._types import AssosiatedEventInfo, HookDict, HookEventTypes
7
5
  from .hook_event import HookEvent
8
6
  from .hook_registry import HookRegistry
9
-
10
- global_hook_logger = DataLogger(
11
- persist_dir="./data/logs",
12
- subfolder="hooks",
13
- file_prefix="hook",
14
- capacity=1000,
15
- )
16
-
7
+ from .hooked_event import HookedEvent, global_hook_logger
17
8
 
18
9
  __all__ = (
19
10
  "HookEventTypes",
@@ -22,4 +13,5 @@ __all__ = (
22
13
  "HookEvent",
23
14
  "HookRegistry",
24
15
  "global_hook_logger",
16
+ "HookedEvent",
25
17
  )
@@ -37,6 +37,7 @@ class HookDict(TypedDict):
37
37
 
38
38
 
39
39
  StreamHandlers = dict[str, Callable[[SC], Awaitable[None]]]
40
+ """Mapping of chunk type names to their respective asynchronous handler functions."""
40
41
 
41
42
 
42
43
  class AssosiatedEventInfo(TypedDict, total=False):
@@ -0,0 +1,142 @@
1
+ # Copyright (c) 2025, HaiyangLi <quantocean.li at gmail dot com>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import anyio
6
+ from pydantic import PrivateAttr
7
+
8
+ from lionagi.ln import get_cancelled_exc_class
9
+ from lionagi.protocols.types import DataLogger, Event, EventStatus, Log
10
+ from lionagi.service.hooks import HookEvent, HookEventTypes
11
+
12
+ global_hook_logger = DataLogger(
13
+ persist_dir="./data/logs",
14
+ subfolder="hooks",
15
+ file_prefix="hook",
16
+ capacity=100,
17
+ )
18
+
19
+
20
+ class HookedEvent(Event):
21
+ """Handles asynchronous API calls with automatic token usage tracking.
22
+
23
+ This class manages API calls through endpoints, handling both regular
24
+ and streaming responses with optional token usage tracking.
25
+ """
26
+
27
+ _pre_invoke_hook_event: HookEvent = PrivateAttr(None)
28
+ _post_invoke_hook_event: HookEvent = PrivateAttr(None)
29
+
30
+ async def _stream(self):
31
+ raise NotImplementedError
32
+
33
+ async def _invoke(self):
34
+ raise NotImplementedError
35
+
36
+ async def invoke(self) -> None:
37
+ """Execute the API call through the endpoint.
38
+
39
+ Updates execution status and stores the response or error.
40
+ """
41
+ start = anyio.current_time()
42
+
43
+ try:
44
+ self.execution.status = EventStatus.PROCESSING
45
+ if h_ev := self._pre_invoke_hook_event:
46
+ await h_ev.invoke()
47
+ if h_ev._should_exit:
48
+ raise h_ev._exit_cause or RuntimeError(
49
+ "Pre-invocation hook requested exit without a cause"
50
+ )
51
+ await global_hook_logger.alog(Log.create(h_ev))
52
+
53
+ response = await self._invoke()
54
+
55
+ if h_ev := self._post_invoke_hook_event:
56
+ await h_ev.invoke()
57
+ if h_ev._should_exit:
58
+ raise h_ev._exit_cause or RuntimeError(
59
+ "Post-invocation hook requested exit without a cause"
60
+ )
61
+ await global_hook_logger.alog(Log.create(h_ev))
62
+
63
+ self.execution.response = response
64
+ self.execution.status = EventStatus.COMPLETED
65
+
66
+ except get_cancelled_exc_class():
67
+ self.execution.error = "Invocation cancelled"
68
+ self.execution.status = EventStatus.CANCELLED
69
+ raise
70
+
71
+ except Exception as e:
72
+ self.execution.error = str(e)
73
+ self.execution.status = EventStatus.FAILED
74
+
75
+ finally:
76
+ self.execution.duration = anyio.current_time() - start
77
+
78
+ async def stream(self):
79
+ """Stream the API response through the endpoint.
80
+
81
+ Yields:
82
+ Streaming chunks from the API.
83
+ """
84
+ start = anyio.current_time()
85
+
86
+ response = []
87
+
88
+ try:
89
+ self.execution.status = EventStatus.PROCESSING
90
+
91
+ async for chunk in self._stream():
92
+ response.append(chunk)
93
+ yield chunk
94
+
95
+ self.execution.response = response
96
+ self.execution.status = EventStatus.COMPLETED
97
+
98
+ except get_cancelled_exc_class():
99
+ self.execution.error = "Streaming cancelled"
100
+ self.execution.status = EventStatus.CANCELLED
101
+ raise
102
+
103
+ except Exception as e:
104
+ self.execution.error = str(e)
105
+ self.execution.status = EventStatus.FAILED
106
+
107
+ finally:
108
+ self.execution.duration = anyio.current_time() - start
109
+
110
+ def create_pre_invoke_hook(
111
+ self,
112
+ hook_registry,
113
+ exit_hook: bool = None,
114
+ hook_timeout: float = 30.0,
115
+ hook_params: dict = None,
116
+ ):
117
+ h_ev = HookEvent(
118
+ hook_type=HookEventTypes.PreInvokation,
119
+ event_like=self,
120
+ registry=hook_registry,
121
+ exit=exit_hook,
122
+ timeout=hook_timeout,
123
+ params=hook_params or {},
124
+ )
125
+ self._pre_invoke_hook_event = h_ev
126
+
127
+ def create_post_invoke_hook(
128
+ self,
129
+ hook_registry,
130
+ exit_hook: bool = None,
131
+ hook_timeout: float = 30.0,
132
+ hook_params: dict = None,
133
+ ):
134
+ h_ev = HookEvent(
135
+ hook_type=HookEventTypes.PostInvokation,
136
+ event_like=self,
137
+ registry=hook_registry,
138
+ exit=exit_hook,
139
+ timeout=hook_timeout,
140
+ params=hook_params or {},
141
+ )
142
+ self._post_invoke_hook_event = h_ev
lionagi/service/imodel.py CHANGED
@@ -7,10 +7,10 @@ from collections.abc import AsyncGenerator, Callable
7
7
 
8
8
  from pydantic import BaseModel
9
9
 
10
+ from lionagi.ln import is_coro_func, now_utc
10
11
  from lionagi.protocols.generic.log import Log
11
12
  from lionagi.protocols.types import ID, Event, EventStatus, IDType
12
13
  from lionagi.service.hooks.hook_event import HookEventTypes
13
- from lionagi.utils import is_coro_func, time
14
14
 
15
15
  from .connections.api_calling import APICalling
16
16
  from .connections.endpoint import Endpoint
@@ -106,7 +106,7 @@ class iModel:
106
106
  raise ValueError("created_at must be a float timestamp.")
107
107
  self.created_at = created_at
108
108
  else:
109
- self.created_at = time()
109
+ self.created_at = now_utc().timestamp()
110
110
 
111
111
  # 2. Configure Endpoint ---------------------------------------------
112
112
  model = kwargs.get("model", None)
lionagi/session/branch.py CHANGED
@@ -3,15 +3,14 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
5
  from collections.abc import AsyncGenerator, Callable
6
- from enum import Enum
7
6
  from typing import TYPE_CHECKING, Any, Literal, Optional
8
7
 
9
8
  from pydantic import BaseModel, Field, JsonValue, PrivateAttr, field_serializer
10
9
 
11
10
  from lionagi.config import settings
12
- from lionagi.fields import Instruct
13
- from lionagi.libs.schema.as_readable import as_readable
11
+ from lionagi.ln.types import Unset
14
12
  from lionagi.models.field_model import FieldModel
13
+ from lionagi.operations.flow import AlcallParams
15
14
  from lionagi.operations.manager import OperationManager
16
15
  from lionagi.protocols.action.manager import ActionManager
17
16
  from lionagi.protocols.action.tool import FuncTool, Tool, ToolRef
@@ -42,21 +41,22 @@ from lionagi.protocols.types import (
42
41
  )
43
42
  from lionagi.service.connections.endpoint import Endpoint
44
43
  from lionagi.service.types import iModel, iModelManager
45
- from lionagi.settings import Settings
46
44
  from lionagi.tools.base import LionTool
47
- from lionagi.utils import UNDEFINED
48
- from lionagi.utils import alcall as alcall_legacy
49
45
  from lionagi.utils import copy
50
46
 
51
47
  from .prompts import LION_SYSTEM_MESSAGE
52
48
 
53
49
  if TYPE_CHECKING:
50
+ from lionagi.fields import Instruct
54
51
  from lionagi.protocols.operatives.operative import Operative
55
52
 
56
53
 
57
54
  __all__ = ("Branch",)
58
55
 
59
56
 
57
+ _DEFAULT_ALCALL_PARAMS = None
58
+
59
+
60
60
  class Branch(Element, Communicatable, Relational):
61
61
  """
62
62
  Manages a conversation 'branch' with messages, tools, and iModels.
@@ -150,10 +150,10 @@ class Branch(Element, Communicatable, Relational):
150
150
  Sender to attribute to the system message if it is added.
151
151
  chat_model (iModel, optional):
152
152
  The primary "chat" iModel for conversation. If not provided,
153
- a default from `Settings.iModel.CHAT` is used.
153
+ uses default provider and model from settings.
154
154
  parse_model (iModel, optional):
155
155
  The "parse" iModel for structured data parsing.
156
- Defaults to `Settings.iModel.PARSE`.
156
+ Defaults to chat_model if not provided.
157
157
  imodel (iModel, optional):
158
158
  Deprecated. Alias for `chat_model`.
159
159
  tools (FuncTool | list[FuncTool], optional):
@@ -233,7 +233,7 @@ class Branch(Element, Communicatable, Relational):
233
233
  log_config = LogManagerConfig(**log_config)
234
234
  self._log_manager = LogManager.from_config(log_config, logs=logs)
235
235
  else:
236
- self._log_manager = LogManager(**Settings.Config.LOG, logs=logs)
236
+ self._log_manager = LogManager(**settings.LOG_CONFIG, logs=logs)
237
237
 
238
238
  self._operation_manager = OperationManager()
239
239
 
@@ -703,12 +703,12 @@ class Branch(Element, Communicatable, Relational):
703
703
  Branch: A new `Branch` instance based on the deserialized data.
704
704
  """
705
705
  dict_ = {
706
- "messages": data.pop("messages", UNDEFINED),
707
- "logs": data.pop("logs", UNDEFINED),
708
- "chat_model": data.pop("chat_model", UNDEFINED),
709
- "parse_model": data.pop("parse_model", UNDEFINED),
710
- "system": data.pop("system", UNDEFINED),
711
- "log_config": data.pop("log_config", UNDEFINED),
706
+ "messages": data.pop("messages", Unset),
707
+ "logs": data.pop("logs", Unset),
708
+ "chat_model": data.pop("chat_model", Unset),
709
+ "parse_model": data.pop("parse_model", Unset),
710
+ "system": data.pop("system", Unset),
711
+ "log_config": data.pop("log_config", Unset),
712
712
  }
713
713
  params = {}
714
714
 
@@ -721,8 +721,8 @@ class Branch(Element, Communicatable, Relational):
721
721
  params[k] = v
722
722
 
723
723
  params.update(dict_)
724
- # Remove placeholders (UNDEFINED) so we don't incorrectly assign them
725
- return cls(**{k: v for k, v in params.items() if v is not UNDEFINED})
724
+ # Remove placeholders (Unset) so we don't incorrectly assign them
725
+ return cls(**{k: v for k, v in params.items() if v is not Unset})
726
726
 
727
727
  def dump_logs(self, clear: bool = True, persist_path=None):
728
728
  """
@@ -913,7 +913,7 @@ class Branch(Element, Communicatable, Relational):
913
913
  async def operate(
914
914
  self,
915
915
  *,
916
- instruct: Instruct = None,
916
+ instruct: "Instruct" = None,
917
917
  instruction: Instruction | JsonValue = None,
918
918
  guidance: JsonValue = None,
919
919
  context: JsonValue = None,
@@ -934,7 +934,7 @@ class Branch(Element, Communicatable, Relational):
934
934
  ] = None, # alias of operative.request_type
935
935
  actions: bool = False,
936
936
  reason: bool = False,
937
- action_kwargs: dict = None,
937
+ call_params: AlcallParams = None,
938
938
  action_strategy: Literal["sequential", "concurrent"] = "concurrent",
939
939
  verbose_action: bool = False,
940
940
  field_models: list[FieldModel] = None,
@@ -1001,8 +1001,6 @@ class Branch(Element, Communicatable, Relational):
1001
1001
  If `True`, signals that function-calling or "action" usage is expected.
1002
1002
  reason (bool, optional):
1003
1003
  If `True`, signals that the LLM should provide chain-of-thought or reasoning (where applicable).
1004
- action_kwargs (dict | None, optional):
1005
- Additional parameters for the `branch.act()` call if tools are invoked.
1006
1004
  action_strategy (Literal["sequential","concurrent"], optional):
1007
1005
  The strategy for invoking tools (default: "concurrent").
1008
1006
  verbose_action (bool, optional):
@@ -1052,7 +1050,7 @@ class Branch(Element, Communicatable, Relational):
1052
1050
  response_format=response_format,
1053
1051
  actions=actions,
1054
1052
  reason=reason,
1055
- action_kwargs=action_kwargs,
1053
+ call_params=call_params,
1056
1054
  action_strategy=action_strategy,
1057
1055
  verbose_action=verbose_action,
1058
1056
  field_models=field_models,
@@ -1162,21 +1160,9 @@ class Branch(Element, Communicatable, Relational):
1162
1160
  async def _act(
1163
1161
  self,
1164
1162
  action_request: ActionRequest | BaseModel | dict,
1165
- suppress_errors: bool = False,
1166
- verbose_action: bool = False,
1163
+ suppress_errors: bool,
1164
+ verbose_action: bool,
1167
1165
  ) -> ActionResponse:
1168
- """
1169
- Internal method to invoke a tool (action) asynchronously.
1170
-
1171
- Args:
1172
- action_request (ActionRequest|BaseModel|dict):
1173
- Must contain `function` and `arguments`.
1174
- suppress_errors (bool, optional):
1175
- If True, errors are logged instead of raised.
1176
-
1177
- Returns:
1178
- ActionResponse: Result of the tool invocation or `None` if suppressed.
1179
- """
1180
1166
  from lionagi.operations._act.act import _act
1181
1167
 
1182
1168
  return await _act(
@@ -1193,103 +1179,38 @@ class Branch(Element, Communicatable, Relational):
1193
1179
  strategy: Literal["concurrent", "sequential"] = "concurrent",
1194
1180
  verbose_action: bool = False,
1195
1181
  suppress_errors: bool = True,
1196
- sanitize_input: bool = False,
1197
- unique_input: bool = False,
1198
- num_retries: int = 0,
1199
- initial_delay: float = 0,
1200
- retry_delay: float = 0,
1201
- backoff_factor: float = 1,
1202
- retry_default: Any = UNDEFINED,
1203
- retry_timeout: float | None = None,
1204
- max_concurrent: int | None = None,
1205
- throttle_period: float | None = None,
1206
- flatten: bool = True,
1207
- dropna: bool = True,
1208
- unique_output: bool = False,
1209
- flatten_tuple_set: bool = False,
1182
+ call_params: AlcallParams = None,
1210
1183
  ) -> list[ActionResponse]:
1211
- """
1212
- Public, potentially batched, asynchronous interface to run one or multiple action requests.
1213
-
1214
- Args:
1215
- action_request (list|ActionRequest|BaseModel|dict):
1216
- A single or list of action requests, each requiring
1217
- `function` and `arguments`.
1218
- strategy (Literal["concurrent","sequential","batch"]):
1219
- The execution strategy to use.
1220
- verbose_action (bool):
1221
- If True, log detailed information about the action.
1222
- suppress_errors (bool):
1223
- If True, log errors instead of raising exceptions.
1224
- sanitize_input (bool):
1225
- Reserved. Potentially sanitize the action arguments.
1226
- unique_input (bool):
1227
- Reserved. Filter out duplicate requests.
1228
- num_retries (int):
1229
- Number of times to retry on failure (default 0).
1230
- initial_delay (float):
1231
- Delay before first attempt (seconds).
1232
- retry_delay (float):
1233
- Base delay between retries.
1234
- backoff_factor (float):
1235
- Multiplier for the `retry_delay` after each attempt.
1236
- retry_default (Any):
1237
- Fallback value if all retries fail (if suppressing errors).
1238
- retry_timeout (float|None):
1239
- Overall timeout for all attempts (None = no limit).
1240
- max_concurrent (int|None):
1241
- Maximum concurrent tasks.
1242
- throttle_period (float|None):
1243
- Minimum spacing (in seconds) between requests.
1244
- flatten (bool):
1245
- If a list of results is returned, flatten them if possible.
1246
- dropna (bool):
1247
- Remove `None` or invalid results from final output if True.
1248
- unique_output (bool):
1249
- Only return unique results if True.
1250
- flatten_tuple_set (bool):
1251
- Flatten nested tuples in results if True.
1184
+ global _DEFAULT_ALCALL_PARAMS
1185
+ if call_params is None:
1186
+ if _DEFAULT_ALCALL_PARAMS is None:
1187
+ _DEFAULT_ALCALL_PARAMS = AlcallParams(output_dropna=True)
1188
+ call_params = _DEFAULT_ALCALL_PARAMS
1189
+
1190
+ kw = {
1191
+ "suppress_errors": suppress_errors,
1192
+ "verbose_action": verbose_action,
1193
+ }
1252
1194
 
1253
- Returns:
1254
- Any:
1255
- The result or results from the invoked tool(s).
1256
- """
1257
1195
  match strategy:
1258
1196
  case "concurrent":
1259
1197
  return await self._concurrent_act(
1260
- action_request,
1261
- verbose_action=verbose_action,
1262
- suppress_errors=suppress_errors,
1263
- sanitize_input=sanitize_input,
1264
- unique_input=unique_input,
1265
- num_retries=num_retries,
1266
- initial_delay=initial_delay,
1267
- retry_delay=retry_delay,
1268
- backoff_factor=backoff_factor,
1269
- retry_default=retry_default,
1270
- retry_timeout=retry_timeout,
1271
- max_concurrent=max_concurrent,
1272
- throttle_period=throttle_period,
1273
- flatten=flatten,
1274
- dropna=dropna,
1275
- unique_output=unique_output,
1276
- flatten_tuple_set=flatten_tuple_set,
1198
+ action_request, call_params, **kw
1277
1199
  )
1278
1200
  case "sequential":
1279
- return await self._sequential_act(
1280
- action_request,
1281
- verbose_action=verbose_action,
1282
- suppress_errors=suppress_errors,
1283
- )
1201
+ return await self._sequential_act(action_request, **kw)
1284
1202
  case _:
1285
- raise
1203
+ raise ValueError(
1204
+ "Invalid strategy. Choose 'concurrent' or 'sequential'."
1205
+ )
1286
1206
 
1287
1207
  async def _concurrent_act(
1288
1208
  self,
1289
1209
  action_request: ActionRequest | BaseModel | dict,
1210
+ call_params: AlcallParams = None,
1290
1211
  **kwargs,
1291
1212
  ) -> list:
1292
- return await alcall_legacy(action_request, self._act, **kwargs)
1213
+ return await call_params(action_request, self._act, **kwargs)
1293
1214
 
1294
1215
  async def _sequential_act(
1295
1216
  self,
@@ -1305,56 +1226,10 @@ class Branch(Element, Communicatable, Relational):
1305
1226
  results = []
1306
1227
  for req in action_request:
1307
1228
  results.append(
1308
- await self._act(
1309
- req,
1310
- verbose_action=verbose_action,
1311
- suppress_errors=suppress_errors,
1312
- )
1229
+ await self._act(req, verbose_action, suppress_errors)
1313
1230
  )
1314
1231
  return results
1315
1232
 
1316
- async def select(
1317
- self,
1318
- instruct: Instruct | dict[str, Any],
1319
- choices: list[str] | type[Enum] | dict[str, Any],
1320
- max_num_selections: int = 1,
1321
- branch_kwargs: dict[str, Any] | None = None,
1322
- verbose: bool = False,
1323
- **kwargs: Any,
1324
- ):
1325
- """
1326
- Performs a selection operation from given choices using an LLM-driven approach.
1327
-
1328
- Args:
1329
- instruct (Instruct|dict[str, Any]):
1330
- The instruction model or dictionary for the LLM call.
1331
- choices (list[str]|type[Enum]|dict[str,Any]):
1332
- The set of options to choose from.
1333
- max_num_selections (int):
1334
- Maximum allowed selections (default = 1).
1335
- branch_kwargs (dict[str, Any]|None):
1336
- Extra arguments to create or configure a new branch if needed.
1337
- verbose (bool):
1338
- If True, prints debug info.
1339
- **kwargs:
1340
- Additional arguments for the underlying `operate()` call.
1341
-
1342
- Returns:
1343
- Any:
1344
- A `SelectionModel` or similar that indicates the user's choice(s).
1345
- """
1346
- from lionagi.operations.select.select import select
1347
-
1348
- return await select(
1349
- branch=self,
1350
- instruct=instruct,
1351
- choices=choices,
1352
- max_num_selections=max_num_selections,
1353
- branch_kwargs=branch_kwargs,
1354
- verbose=verbose,
1355
- **kwargs,
1356
- )
1357
-
1358
1233
  async def interpret(
1359
1234
  self,
1360
1235
  text: str,
@@ -1414,7 +1289,7 @@ class Branch(Element, Communicatable, Relational):
1414
1289
 
1415
1290
  async def instruct(
1416
1291
  self,
1417
- instruct: Instruct,
1292
+ instruct: "Instruct",
1418
1293
  /,
1419
1294
  **kwargs,
1420
1295
  ):
@@ -1441,7 +1316,7 @@ class Branch(Element, Communicatable, Relational):
1441
1316
 
1442
1317
  async def ReAct(
1443
1318
  self,
1444
- instruct: Instruct | dict[str, Any],
1319
+ instruct: "Instruct | dict[str, Any]",
1445
1320
  interpret: bool = False,
1446
1321
  interpret_domain: str | None = None,
1447
1322
  interpret_style: str | None = None,
@@ -1569,7 +1444,7 @@ class Branch(Element, Communicatable, Relational):
1569
1444
 
1570
1445
  async def ReActStream(
1571
1446
  self,
1572
- instruct: Instruct | dict[str, Any],
1447
+ instruct: "Instruct | dict[str, Any]",
1573
1448
  interpret: bool = False,
1574
1449
  interpret_domain: str | None = None,
1575
1450
  interpret_style: str | None = None,
@@ -1621,6 +1496,8 @@ class Branch(Element, Communicatable, Relational):
1621
1496
  ):
1622
1497
  analysis, str_ = result
1623
1498
  if verbose:
1499
+ from lionagi.libs.schema.as_readable import as_readable
1500
+
1624
1501
  str_ += "\n---------\n"
1625
1502
  as_readable(str_, md=True, display_str=True)
1626
1503
  yield analysis
@@ -416,48 +416,5 @@ class Session(Node, Communicatable, Relational):
416
416
  alcall_params=alcall_params,
417
417
  )
418
418
 
419
- def cleanup_memory(
420
- self, clear_branches: bool = True, clear_mail: bool = True
421
- ):
422
- """
423
- Clean up session memory to prevent memory accumulation.
424
-
425
- Args:
426
- clear_branches: Whether to clear branch logs and memory
427
- clear_mail: Whether to clear mail transfer history
428
- """
429
- if clear_branches and self.branches:
430
- for branch in self.branches:
431
- if hasattr(branch, "dump_logs"):
432
- branch.dump_logs(clear=True)
433
-
434
- if clear_mail and self.mail_transfer:
435
- # Clear mail transfer history if available
436
- if hasattr(self.mail_transfer, "clear"):
437
- self.mail_transfer.clear()
438
-
439
- async def acleanup_memory(
440
- self, clear_branches: bool = True, clear_mail: bool = True
441
- ):
442
- """
443
- Asynchronously clean up session memory to prevent memory accumulation.
444
-
445
- Args:
446
- clear_branches: Whether to clear branch logs and memory
447
- clear_mail: Whether to clear mail transfer history
448
- """
449
- if clear_branches and self.branches:
450
- for branch in self.branches:
451
- if hasattr(branch, "adump_logs"):
452
- await branch.adump_logs(clear=True)
453
-
454
- if clear_mail and self.mail_transfer:
455
- # Clear mail transfer history if available
456
- if hasattr(self.mail_transfer, "aclear"):
457
- await self.mail_transfer.aclear()
458
- elif hasattr(self.mail_transfer, "clear"):
459
- self.mail_transfer.clear()
460
-
461
419
 
462
- __all__ = ["Session"]
463
- # File: autoos/session/session.py
420
+ __all__ = ("Session",)