lionagi 0.10.1__py3-none-any.whl → 0.10.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.
Files changed (33) hide show
  1. lionagi/_types.py +1 -0
  2. lionagi/adapters/toml_adapter.py +1 -1
  3. lionagi/fields/__init__.py +29 -0
  4. lionagi/{libs/fields → fields}/action.py +0 -2
  5. lionagi/fields/base.py +151 -0
  6. lionagi/{libs/fields → fields}/file.py +71 -40
  7. lionagi/{libs/fields → fields}/instruct.py +1 -3
  8. lionagi/{libs/fields → fields}/reason.py +4 -19
  9. lionagi/libs/package/imports.py +17 -162
  10. lionagi/libs/token_transform/synthlang_/base.py +1 -1
  11. lionagi/libs/token_transform/synthlang_/resources/frameworks/framework_options.json +1 -1
  12. lionagi/libs/token_transform/synthlang_/resources/mapping/lion_emoji_mapping.toml +1 -1
  13. lionagi/libs/token_transform/synthlang_/resources/mapping/python_math_mapping.toml +1 -1
  14. lionagi/libs/token_transform/synthlang_/resources/mapping/rust_chinese_mapping.toml +1 -1
  15. lionagi/operations/ReAct/ReAct.py +1 -1
  16. lionagi/operations/ReAct/utils.py +36 -15
  17. lionagi/operations/_act/act.py +1 -1
  18. lionagi/operations/brainstorm/brainstorm.py +1 -1
  19. lionagi/operations/instruct/instruct.py +1 -1
  20. lionagi/operations/operate/operate.py +1 -1
  21. lionagi/operations/plan/plan.py +1 -1
  22. lionagi/operations/select/select.py +1 -1
  23. lionagi/operations/utils.py +1 -1
  24. lionagi/protocols/action/manager.py +1 -1
  25. lionagi/protocols/operatives/step.py +2 -2
  26. lionagi/session/branch.py +6 -60
  27. lionagi/utils.py +214 -0
  28. lionagi/version.py +1 -1
  29. {lionagi-0.10.1.dist-info → lionagi-0.10.3.dist-info}/METADATA +3 -3
  30. {lionagi-0.10.1.dist-info → lionagi-0.10.3.dist-info}/RECORD +32 -31
  31. lionagi/libs/fields/__init__.py +0 -36
  32. {lionagi-0.10.1.dist-info → lionagi-0.10.3.dist-info}/WHEEL +0 -0
  33. {lionagi-0.10.1.dist-info → lionagi-0.10.3.dist-info}/licenses/LICENSE +0 -0
@@ -4,34 +4,45 @@
4
4
 
5
5
  from typing import ClassVar, Literal
6
6
 
7
- from pydantic import BaseModel, Field
7
+ from pydantic import Field, field_validator
8
8
 
9
+ from lionagi.models import HashableModel
9
10
 
10
- class PlannedAction(BaseModel):
11
+
12
+ class PlannedAction(HashableModel):
11
13
  """
12
- Short descriptor for an upcoming action/tool invocation the LLM wants to perform.
13
- The model can hold multiple actions in a single round if needed.
14
+ Short descriptor for an upcoming action/tool invocation the LLM wants to
15
+ perform. The model can hold multiple actions in a single round if needed.
14
16
  """
15
17
 
16
- action_type: str = Field(
17
- ...,
18
- description="The name or type of tool/action to invoke (e.g., 'search_exa', 'reader_tool').",
18
+ action_type: str | None = Field(
19
+ default=None,
20
+ description=(
21
+ "The name or type of tool/action to invoke. "
22
+ "(e.g., 'search_exa', 'reader_tool')"
23
+ ),
19
24
  )
20
- description: str = Field(
21
- ...,
22
- description="A short explanation of why or what is intended to achieve with this action.",
25
+ description: str | None = Field(
26
+ default=None,
27
+ description=(
28
+ "A short description of the action to perform. "
29
+ "This should be a concise summary of what the action entails."
30
+ "Also include your rationale for this action, if applicable."
31
+ ),
23
32
  )
24
33
 
25
34
 
26
- class ReActAnalysis(BaseModel):
35
+ class ReActAnalysis(HashableModel):
27
36
  """
28
37
  Captures the ReAct chain-of-thought output each round:
29
38
  1) The LLM's 'analysis' (reasoning),
30
39
  2) A list of planned actions to perform before finalizing,
31
40
  3) Indication whether more expansions/rounds are needed,
32
41
  4) Additional tuning knobs: how to handle validation, how to execute actions, etc.
33
- Remember do not repeat yourself, and aim to use the most efficient way to achieve
34
- the goal to user's satisfaction.
42
+
43
+ Note:
44
+ - Retain from repeating yourself
45
+ - use the most efficient way to achieve the goal to user's satisfaction
35
46
  """
36
47
 
37
48
  # Standard ReAct strings for controlling expansions:
@@ -101,6 +112,16 @@ class ReActAnalysis(BaseModel):
101
112
  )
102
113
 
103
114
 
104
- class Analysis(BaseModel):
115
+ class Analysis(HashableModel):
116
+
117
+ answer: str | None = None
105
118
 
106
- answer: str
119
+ @field_validator("answer", mode="before")
120
+ def _validate_answer(cls, value):
121
+ if not value:
122
+ return None
123
+ if isinstance(value, str) and not value.strip():
124
+ return None
125
+ if not isinstance(value, str):
126
+ raise ValueError("Answer must be a non-empty string.")
127
+ return value.strip()
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
7
7
 
8
8
  from pydantic import BaseModel
9
9
 
10
- from lionagi.libs.fields.action import ActionResponseModel
10
+ from lionagi.fields.action import ActionResponseModel
11
11
  from lionagi.protocols.types import ActionRequest, Log
12
12
 
13
13
  if TYPE_CHECKING:
@@ -7,7 +7,7 @@ from typing import Any, Literal
7
7
 
8
8
  from pydantic import BaseModel
9
9
 
10
- from lionagi.libs.fields.instruct import (
10
+ from lionagi.fields.instruct import (
11
11
  LIST_INSTRUCT_FIELD_MODEL,
12
12
  Instruct,
13
13
  InstructResponse,
@@ -4,7 +4,7 @@
4
4
 
5
5
  from typing import TYPE_CHECKING, Any
6
6
 
7
- from lionagi.libs.fields.instruct import Instruct
7
+ from lionagi.fields.instruct import Instruct
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from lionagi.session.branch import Branch
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Literal
7
7
 
8
8
  from pydantic import BaseModel, JsonValue
9
9
 
10
- from lionagi.libs.fields.instruct import Instruct
10
+ from lionagi.fields.instruct import Instruct
11
11
  from lionagi.models import FieldModel, ModelParams
12
12
  from lionagi.protocols.types import (
13
13
  Instruction,
@@ -6,7 +6,7 @@ from typing import Any, Literal
6
6
 
7
7
  from pydantic import BaseModel
8
8
 
9
- from lionagi.libs.fields.instruct import (
9
+ from lionagi.fields.instruct import (
10
10
  LIST_INSTRUCT_FIELD_MODEL,
11
11
  Instruct,
12
12
  InstructResponse,
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Any
7
7
 
8
8
  from pydantic import BaseModel
9
9
 
10
- from lionagi.libs.fields.instruct import Instruct
10
+ from lionagi.fields.instruct import Instruct
11
11
 
12
12
  from .utils import SelectionModel
13
13
 
@@ -2,7 +2,7 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
- from lionagi.libs.fields.instruct import Instruct
5
+ from lionagi.fields.instruct import Instruct
6
6
  from lionagi.session.session import Branch, Session
7
7
 
8
8
 
@@ -4,11 +4,11 @@
4
4
 
5
5
  from typing import Any
6
6
 
7
+ from lionagi.fields.action import ActionRequestModel
7
8
  from lionagi.protocols._concepts import Manager
8
9
  from lionagi.protocols.messages.action_request import ActionRequest
9
10
  from lionagi.utils import to_list
10
11
 
11
- from ...libs.fields.action import ActionRequestModel
12
12
  from .function_calling import FunctionCalling
13
13
  from .tool import FuncTool, FuncToolRef, Tool, ToolRef
14
14
 
@@ -5,14 +5,14 @@
5
5
  from pydantic import BaseModel, Field
6
6
  from pydantic.fields import FieldInfo
7
7
 
8
- from lionagi.libs.fields.action import (
8
+ from lionagi.fields.action import (
9
9
  ACTION_REQUESTS_FIELD,
10
10
  ACTION_REQUIRED_FIELD,
11
11
  ACTION_RESPONSES_FIELD,
12
12
  ActionRequestModel,
13
13
  ActionResponseModel,
14
14
  )
15
- from lionagi.libs.fields.reason import REASON_FIELD, Reason
15
+ from lionagi.fields.reason import REASON_FIELD, Reason
16
16
  from lionagi.models import FieldModel, ModelParams
17
17
  from lionagi.protocols.operatives.operative import Operative
18
18
 
lionagi/session/branch.py CHANGED
@@ -4,25 +4,20 @@
4
4
 
5
5
  from collections.abc import AsyncGenerator
6
6
  from enum import Enum
7
- from typing import TYPE_CHECKING, Any, Literal
7
+ from typing import Any, Literal
8
8
 
9
9
  import pandas as pd
10
10
  from jinja2 import Template
11
11
  from pydantic import BaseModel, Field, JsonValue, PrivateAttr
12
12
 
13
+ from lionagi.fields import Instruct
13
14
  from lionagi.libs.schema.as_readable import as_readable
14
15
  from lionagi.models.field_model import FieldModel
15
- from lionagi.models.model_params import ModelParams
16
- from lionagi.protocols.action.manager import ActionManager
17
16
  from lionagi.protocols.action.tool import FuncTool, Tool, ToolRef
18
- from lionagi.protocols.operatives.operative import Operative
19
-
20
- # Forward reference for Instruct which will be imported in the methods that use it
21
- # to avoid circular imports
22
- Instruct = Any
23
17
  from lionagi.protocols.types import (
24
18
  ID,
25
19
  MESSAGE_FIELDS,
20
+ ActionManager,
26
21
  ActionRequest,
27
22
  ActionResponse,
28
23
  AssistantResponse,
@@ -37,6 +32,7 @@ from lionagi.protocols.types import (
37
32
  Mailbox,
38
33
  MessageManager,
39
34
  MessageRole,
35
+ Operative,
40
36
  Package,
41
37
  PackageCategory,
42
38
  Pile,
@@ -54,10 +50,6 @@ from lionagi.utils import UNDEFINED, alcall, bcall, copy
54
50
 
55
51
  from .prompts import LION_SYSTEM_MESSAGE
56
52
 
57
- if TYPE_CHECKING:
58
- # Forward references for type checking (e.g., in operations or extended modules)
59
- from lionagi.session.branch import Branch
60
-
61
53
  __all__ = ("Branch",)
62
54
 
63
55
 
@@ -907,7 +899,6 @@ class Branch(Element, Communicatable, Relational):
907
899
  sender: SenderRecipient = None,
908
900
  recipient: SenderRecipient = None,
909
901
  progression: Progression = None,
910
- imodel: iModel = None, # deprecated, alias of chat_model
911
902
  chat_model: iModel = None,
912
903
  invoke_actions: bool = True,
913
904
  tool_schemas: list[dict] = None,
@@ -920,7 +911,6 @@ class Branch(Element, Communicatable, Relational):
920
911
  response_format: type[
921
912
  BaseModel
922
913
  ] = None, # alias of operative.request_type
923
- return_operative: bool = False,
924
914
  actions: bool = False,
925
915
  reason: bool = False,
926
916
  action_kwargs: dict = None,
@@ -931,15 +921,9 @@ class Branch(Element, Communicatable, Relational):
931
921
  verbose_action: bool = False,
932
922
  field_models: list[FieldModel] = None,
933
923
  exclude_fields: list | dict | None = None,
934
- request_params: ModelParams = None,
935
- request_param_kwargs: dict = None,
936
- response_params: ModelParams = None,
937
- response_param_kwargs: dict = None,
938
924
  handle_validation: Literal[
939
925
  "raise", "return_value", "return_none"
940
926
  ] = "return_value",
941
- operative_model: type[BaseModel] = None,
942
- request_model: type[BaseModel] = None,
943
927
  include_token_usage_to_model: bool = False,
944
928
  **kwargs,
945
929
  ) -> list | BaseModel | None | dict | str:
@@ -973,8 +957,7 @@ class Branch(Element, Communicatable, Relational):
973
957
  The recipient ID for newly added messages.
974
958
  progression (Progression, optional):
975
959
  Custom ordering of conversation messages.
976
- imodel (iModel, deprecated):
977
- Alias of `chat_model`.
960
+
978
961
  chat_model (iModel, optional):
979
962
  The LLM used for the main chat operation. Defaults to `branch.chat_model`.
980
963
  invoke_actions (bool, optional):
@@ -995,8 +978,6 @@ class Branch(Element, Communicatable, Relational):
995
978
  If provided, reuses an existing operative's config for parsing/validation.
996
979
  response_format (type[BaseModel], optional):
997
980
  Expected Pydantic model for the final response (alias for `operative.request_type`).
998
- return_operative (bool, optional):
999
- If `True`, returns the entire `Operative` object after processing
1000
981
  rather than the structured or raw output.
1001
982
  actions (bool, optional):
1002
983
  If `True`, signals that function-calling or "action" usage is expected.
@@ -1014,20 +995,8 @@ class Branch(Element, Communicatable, Relational):
1014
995
  Field-level definitions or overrides for the model schema.
1015
996
  exclude_fields (list|dict|None, optional):
1016
997
  Which fields to exclude from final validation or model building.
1017
- request_params (ModelParams | None, optional):
1018
- Extra config for building the request model in the operative.
1019
- request_param_kwargs (dict|None, optional):
1020
- Additional kwargs passed to the `ModelParams` constructor for the request.
1021
- response_params (ModelParams | None, optional):
1022
- Config for building the response model after actions.
1023
- response_param_kwargs (dict|None, optional):
1024
- Additional kwargs passed to the `ModelParams` constructor for the response.
1025
998
  handle_validation (Literal["raise","return_value","return_none"], optional):
1026
999
  How to handle parsing failures (default: "return_value").
1027
- operative_model (type[BaseModel], deprecated):
1028
- Alias for `response_format`.
1029
- request_model (type[BaseModel], optional):
1030
- Another alias for `response_format`.
1031
1000
  include_token_usage_to_model:
1032
1001
  If `True`, includes token usage in the model messages.
1033
1002
  **kwargs:
@@ -1065,7 +1034,6 @@ class Branch(Element, Communicatable, Relational):
1065
1034
  tools=tools,
1066
1035
  operative=operative,
1067
1036
  response_format=response_format,
1068
- return_operative=return_operative,
1069
1037
  actions=actions,
1070
1038
  reason=reason,
1071
1039
  action_kwargs=action_kwargs,
@@ -1074,14 +1042,7 @@ class Branch(Element, Communicatable, Relational):
1074
1042
  verbose_action=verbose_action,
1075
1043
  field_models=field_models,
1076
1044
  exclude_fields=exclude_fields,
1077
- request_params=request_params,
1078
- request_param_kwargs=request_param_kwargs,
1079
- response_params=response_params,
1080
- response_param_kwargs=response_param_kwargs,
1081
1045
  handle_validation=handle_validation,
1082
- operative_model=operative_model,
1083
- request_model=request_model,
1084
- imodel=imodel,
1085
1046
  include_token_usage_to_model=include_token_usage_to_model,
1086
1047
  **kwargs,
1087
1048
  )
@@ -1096,19 +1057,15 @@ class Branch(Element, Communicatable, Relational):
1096
1057
  sender: SenderRecipient = None,
1097
1058
  recipient: SenderRecipient = None,
1098
1059
  progression: ID.IDSeq = None,
1099
- request_model: type[BaseModel] | BaseModel | None = None,
1100
1060
  response_format: type[BaseModel] = None,
1101
1061
  request_fields: dict | list[str] = None,
1102
- imodel: iModel = None, # alias of chat_model
1103
1062
  chat_model: iModel = None,
1104
1063
  parse_model: iModel = None,
1105
1064
  skip_validation: bool = False,
1106
1065
  images: list = None,
1107
1066
  image_detail: Literal["low", "high", "auto"] = None,
1108
1067
  num_parse_retries: int = 3,
1109
- fuzzy_match_kwargs: dict = None,
1110
1068
  clear_messages: bool = False,
1111
- operative_model: type[BaseModel] = None,
1112
1069
  include_token_usage_to_model: bool = False,
1113
1070
  **kwargs,
1114
1071
  ):
@@ -1135,14 +1092,10 @@ class Branch(Element, Communicatable, Relational):
1135
1092
  Recipient ID (defaults to `self.id`).
1136
1093
  progression (ID.IDSeq, optional):
1137
1094
  Custom ordering of messages.
1138
- request_model (type[BaseModel] | BaseModel | None, optional):
1139
- Model for validating or structuring the LLM's response.
1140
1095
  response_format (type[BaseModel], optional):
1141
1096
  Alias for `request_model`. If both are provided, raises ValueError.
1142
1097
  request_fields (dict|list[str], optional):
1143
1098
  If you only need certain fields from the LLM's response.
1144
- imodel (iModel, optional):
1145
- Deprecated alias for `chat_model`.
1146
1099
  chat_model (iModel, optional):
1147
1100
  An alternative to the default chat model.
1148
1101
  parse_model (iModel, optional):
@@ -1155,12 +1108,8 @@ class Branch(Element, Communicatable, Relational):
1155
1108
  Image detail level (if used).
1156
1109
  num_parse_retries (int, optional):
1157
1110
  Maximum parsing retries (capped at 5).
1158
- fuzzy_match_kwargs (dict, optional):
1159
- Additional settings for fuzzy field matching (if used).
1160
1111
  clear_messages (bool, optional):
1161
1112
  Whether to clear stored messages before sending.
1162
- operative_model (type[BaseModel], optional):
1163
- Deprecated, alias for `response_format`.
1164
1113
  **kwargs:
1165
1114
  Additional arguments for the underlying LLM call.
1166
1115
 
@@ -1182,18 +1131,15 @@ class Branch(Element, Communicatable, Relational):
1182
1131
  sender=sender,
1183
1132
  recipient=recipient,
1184
1133
  progression=progression,
1185
- request_model=request_model,
1186
1134
  response_format=response_format,
1187
1135
  request_fields=request_fields,
1188
- chat_model=kwargs.pop("chat_model", None) or chat_model or imodel,
1136
+ chat_model=chat_model,
1189
1137
  parse_model=parse_model,
1190
1138
  skip_validation=skip_validation,
1191
1139
  images=images,
1192
1140
  image_detail=image_detail,
1193
1141
  num_parse_retries=num_parse_retries,
1194
- fuzzy_match_kwargs=fuzzy_match_kwargs,
1195
1142
  clear_messages=clear_messages,
1196
- operative_model=operative_model,
1197
1143
  include_token_usage_to_model=include_token_usage_to_model,
1198
1144
  **kwargs,
1199
1145
  )
lionagi/utils.py CHANGED
@@ -6,6 +6,8 @@ import asyncio
6
6
  import contextlib
7
7
  import copy as _copy
8
8
  import functools
9
+ import importlib.metadata
10
+ import importlib.util
9
11
  import json
10
12
  import logging
11
13
  import re
@@ -2367,3 +2369,215 @@ def run_package_manager_command(
2367
2369
  check=True,
2368
2370
  capture_output=True,
2369
2371
  )
2372
+
2373
+
2374
+ def check_import(
2375
+ package_name: str,
2376
+ module_name: str | None = None,
2377
+ import_name: str | None = None,
2378
+ pip_name: str | None = None,
2379
+ attempt_install: bool = True,
2380
+ error_message: str = "",
2381
+ ):
2382
+ """
2383
+ Check if a package is installed, attempt to install if not.
2384
+
2385
+ Args:
2386
+ package_name: The name of the package to check.
2387
+ module_name: The specific module to import (if any).
2388
+ import_name: The specific name to import from the module (if any).
2389
+ pip_name: The name to use for pip installation (if different).
2390
+ attempt_install: Whether to attempt installation if not found.
2391
+ error_message: Custom error message to use if package not found.
2392
+
2393
+ Raises:
2394
+ ImportError: If the package is not found and not installed.
2395
+ ValueError: If the import fails after installation attempt.
2396
+ """
2397
+ if not is_import_installed(package_name):
2398
+ if attempt_install:
2399
+ logging.info(
2400
+ f"Package {package_name} not found. Attempting " "to install.",
2401
+ )
2402
+ try:
2403
+ return install_import(
2404
+ package_name=package_name,
2405
+ module_name=module_name,
2406
+ import_name=import_name,
2407
+ pip_name=pip_name,
2408
+ )
2409
+ except ImportError as e:
2410
+ raise ValueError(
2411
+ f"Failed to install {package_name}: {e}"
2412
+ ) from e
2413
+ else:
2414
+ logging.info(
2415
+ f"Package {package_name} not found. {error_message}",
2416
+ )
2417
+ raise ImportError(
2418
+ f"Package {package_name} not found. {error_message}",
2419
+ )
2420
+
2421
+ return import_module(
2422
+ package_name=package_name,
2423
+ module_name=module_name,
2424
+ import_name=import_name,
2425
+ )
2426
+
2427
+
2428
+ def import_module(
2429
+ package_name: str,
2430
+ module_name: str = None,
2431
+ import_name: str | list = None,
2432
+ ) -> Any:
2433
+ """
2434
+ Import a module by its path.
2435
+
2436
+ Args:
2437
+ module_path: The path of the module to import.
2438
+
2439
+ Returns:
2440
+ The imported module.
2441
+
2442
+ Raises:
2443
+ ImportError: If the module cannot be imported.
2444
+ """
2445
+ try:
2446
+ full_import_path = (
2447
+ f"{package_name}.{module_name}" if module_name else package_name
2448
+ )
2449
+
2450
+ if import_name:
2451
+ import_name = (
2452
+ [import_name]
2453
+ if not isinstance(import_name, list)
2454
+ else import_name
2455
+ )
2456
+ a = __import__(
2457
+ full_import_path,
2458
+ fromlist=import_name,
2459
+ )
2460
+ if len(import_name) == 1:
2461
+ return getattr(a, import_name[0])
2462
+ return [getattr(a, name) for name in import_name]
2463
+ else:
2464
+ return __import__(full_import_path)
2465
+
2466
+ except ImportError as e:
2467
+ raise ImportError(
2468
+ f"Failed to import module {full_import_path}: {e}"
2469
+ ) from e
2470
+
2471
+
2472
+ def install_import(
2473
+ package_name: str,
2474
+ module_name: str | None = None,
2475
+ import_name: str | None = None,
2476
+ pip_name: str | None = None,
2477
+ ):
2478
+ """
2479
+ Attempt to import a package, installing it if not found.
2480
+
2481
+ Args:
2482
+ package_name: The name of the package to import.
2483
+ module_name: The specific module to import (if any).
2484
+ import_name: The specific name to import from the module (if any).
2485
+ pip_name: The name to use for pip installation (if different).
2486
+
2487
+ Raises:
2488
+ ImportError: If the package cannot be imported or installed.
2489
+ subprocess.CalledProcessError: If pip installation fails.
2490
+ """
2491
+ pip_name = pip_name or package_name
2492
+
2493
+ try:
2494
+ return import_module(
2495
+ package_name=package_name,
2496
+ module_name=module_name,
2497
+ import_name=import_name,
2498
+ )
2499
+ except ImportError:
2500
+ logging.info(f"Installing {pip_name}...")
2501
+ try:
2502
+ run_package_manager_command(["install", pip_name])
2503
+ return import_module(
2504
+ package_name=package_name,
2505
+ module_name=module_name,
2506
+ import_name=import_name,
2507
+ )
2508
+ except subprocess.CalledProcessError as e:
2509
+ raise ImportError(f"Failed to install {pip_name}: {e}") from e
2510
+ except ImportError as e:
2511
+ raise ImportError(
2512
+ f"Failed to import {pip_name} after installation: {e}"
2513
+ ) from e
2514
+
2515
+
2516
+ def is_import_installed(package_name: str) -> bool:
2517
+ """
2518
+ Check if a package is installed.
2519
+
2520
+ Args:
2521
+ package_name: The name of the package to check.
2522
+
2523
+ Returns:
2524
+ bool: True if the package is installed, False otherwise.
2525
+ """
2526
+ return importlib.util.find_spec(package_name) is not None
2527
+
2528
+
2529
+ def read_image_to_base64(image_path: str | Path) -> str:
2530
+ import base64
2531
+
2532
+ import cv2
2533
+
2534
+ image_path = str(image_path)
2535
+ image = cv2.imread(image_path, cv2.COLOR_BGR2RGB)
2536
+
2537
+ if image is None:
2538
+ raise ValueError(f"Could not read image from path: {image_path}")
2539
+
2540
+ file_extension = "." + image_path.split(".")[-1]
2541
+
2542
+ success, buffer = cv2.imencode(file_extension, image)
2543
+ if not success:
2544
+ raise ValueError(f"Could not encode image to {file_extension} format.")
2545
+ encoded_image = base64.b64encode(buffer).decode("utf-8")
2546
+ return encoded_image
2547
+
2548
+
2549
+ def pdf_to_images(
2550
+ pdf_path: str, output_folder: str, dpi: int = 300, fmt: str = "jpeg"
2551
+ ) -> list:
2552
+ """
2553
+ Convert a PDF file into images, one image per page.
2554
+
2555
+ Args:
2556
+ pdf_path (str): Path to the input PDF file.
2557
+ output_folder (str): Directory to save the output images.
2558
+ dpi (int): Dots per inch (resolution) for conversion (default: 300).
2559
+ fmt (str): Image format (default: 'jpeg'). Use 'png' if preferred.
2560
+
2561
+ Returns:
2562
+ list: A list of file paths for the saved images.
2563
+ """
2564
+ import os
2565
+
2566
+ convert_from_path = check_import(
2567
+ "pdf2image", import_name="convert_from_path"
2568
+ )
2569
+
2570
+ # Ensure the output folder exists
2571
+ os.makedirs(output_folder, exist_ok=True)
2572
+
2573
+ # Convert PDF to a list of PIL Image objects
2574
+ images = convert_from_path(pdf_path, dpi=dpi)
2575
+
2576
+ saved_paths = []
2577
+ for i, image in enumerate(images):
2578
+ # Construct the output file name
2579
+ image_file = os.path.join(output_folder, f"page_{i+1}.{fmt}")
2580
+ image.save(image_file, fmt.upper())
2581
+ saved_paths.append(image_file)
2582
+
2583
+ return saved_paths
lionagi/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.10.1"
1
+ __version__ = "0.10.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lionagi
3
- Version: 0.10.1
3
+ Version: 0.10.3
4
4
  Summary: An Intelligence Operating System.
5
5
  Author-email: HaiyangLi <quantocean.li@gmail.com>
6
6
  License: Apache License
@@ -221,11 +221,11 @@ Classifier: Programming Language :: Python :: 3.13
221
221
  Requires-Python: >=3.10
222
222
  Requires-Dist: aiocache>=0.12.0
223
223
  Requires-Dist: aiohttp>=3.11.0
224
- Requires-Dist: jinja2>=3.1.0
224
+ Requires-Dist: jinja2>=3.0.0
225
225
  Requires-Dist: pandas>=2.0.0
226
226
  Requires-Dist: pillow>=10.0.0
227
227
  Requires-Dist: pydantic>=2.0.0
228
- Requires-Dist: python-dotenv>=1.0.1
228
+ Requires-Dist: python-dotenv>=1.1.0
229
229
  Requires-Dist: tiktoken>=0.8.0
230
230
  Requires-Dist: toml>=0.9.0
231
231
  Provides-Extra: llms