lionagi 0.17.5__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.
- lionagi/config.py +26 -0
- lionagi/fields/action.py +5 -3
- lionagi/libs/file/chunk.py +3 -14
- lionagi/libs/file/process.py +10 -92
- lionagi/libs/schema/breakdown_pydantic_annotation.py +45 -0
- lionagi/ln/_async_call.py +6 -6
- lionagi/ln/fuzzy/_fuzzy_match.py +3 -6
- lionagi/ln/fuzzy/_fuzzy_validate.py +3 -4
- lionagi/ln/fuzzy/_string_similarity.py +11 -5
- lionagi/ln/fuzzy/_to_dict.py +19 -19
- lionagi/ln/types.py +15 -0
- lionagi/operations/operate/operate.py +7 -11
- lionagi/operations/parse/parse.py +5 -3
- lionagi/protocols/generic/element.py +3 -6
- lionagi/protocols/generic/event.py +1 -1
- lionagi/protocols/mail/package.py +2 -2
- lionagi/protocols/messages/instruction.py +9 -1
- lionagi/protocols/operatives/operative.py +4 -3
- lionagi/service/broadcaster.py +61 -0
- lionagi/service/connections/api_calling.py +21 -140
- lionagi/service/hooks/__init__.py +2 -10
- lionagi/service/hooks/_types.py +1 -0
- lionagi/service/hooks/hooked_event.py +142 -0
- lionagi/service/imodel.py +2 -2
- lionagi/session/branch.py +46 -169
- lionagi/session/session.py +1 -44
- lionagi/tools/file/reader.py +6 -4
- lionagi/utils.py +3 -334
- lionagi/version.py +1 -1
- {lionagi-0.17.5.dist-info → lionagi-0.17.6.dist-info}/METADATA +2 -2
- {lionagi-0.17.5.dist-info → lionagi-0.17.6.dist-info}/RECORD +33 -37
- lionagi/libs/file/_utils.py +0 -10
- lionagi/libs/file/concat.py +0 -121
- lionagi/libs/file/concat_files.py +0 -85
- lionagi/libs/file/file_ops.py +0 -118
- lionagi/libs/file/save.py +0 -103
- lionagi/ln/concurrency/throttle.py +0 -83
- lionagi/settings.py +0 -71
- {lionagi-0.17.5.dist-info → lionagi-0.17.6.dist-info}/WHEEL +0 -0
- {lionagi-0.17.5.dist-info → lionagi-0.17.6.dist-info}/licenses/LICENSE +0 -0
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.
|
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
|
-
|
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
|
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(**
|
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",
|
707
|
-
"logs": data.pop("logs",
|
708
|
-
"chat_model": data.pop("chat_model",
|
709
|
-
"parse_model": data.pop("parse_model",
|
710
|
-
"system": data.pop("system",
|
711
|
-
"log_config": data.pop("log_config",
|
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 (
|
725
|
-
return cls(**{k: v for k, v in params.items() if v is not
|
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
|
-
|
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
|
-
|
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
|
1166
|
-
verbose_action: bool
|
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
|
-
|
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
|
-
|
1213
|
-
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1218
|
-
|
1219
|
-
|
1220
|
-
|
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
|
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
|
lionagi/session/session.py
CHANGED
@@ -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__ =
|
463
|
-
# File: autoos/session/session.py
|
420
|
+
__all__ = ("Session",)
|
lionagi/tools/file/reader.py
CHANGED
@@ -8,6 +8,7 @@ from enum import Enum
|
|
8
8
|
from pydantic import BaseModel, Field, model_validator
|
9
9
|
|
10
10
|
from lionagi.libs.validate.to_num import to_num
|
11
|
+
from lionagi.ln import is_import_installed
|
11
12
|
from lionagi.protocols.action.tool import Tool
|
12
13
|
from lionagi.service.token_calculator import TokenCalculator
|
13
14
|
|
@@ -173,10 +174,11 @@ class ReaderTool(LionTool):
|
|
173
174
|
system_tool_name = "reader_tool"
|
174
175
|
|
175
176
|
def __init__(self):
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
177
|
+
if not is_import_installed("docling"):
|
178
|
+
raise ImportError(
|
179
|
+
"The 'docling' package is required for this feature. "
|
180
|
+
"Please install it via 'pip install lionagi[reader]'."
|
181
|
+
)
|
180
182
|
|
181
183
|
from docling.document_converter import DocumentConverter
|
182
184
|
|