lionagi 0.15.8__py3-none-any.whl → 0.15.11__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 (42) hide show
  1. lionagi/__init__.py +4 -6
  2. lionagi/adapters/async_postgres_adapter.py +55 -319
  3. lionagi/libs/file/_utils.py +10 -0
  4. lionagi/libs/file/process.py +16 -13
  5. lionagi/libs/file/save.py +3 -2
  6. lionagi/libs/schema/load_pydantic_model_from_schema.py +2 -1
  7. lionagi/libs/unstructured/pdf_to_image.py +2 -2
  8. lionagi/libs/validate/string_similarity.py +4 -4
  9. lionagi/ln/__init__.py +38 -0
  10. lionagi/ln/_extract_json.py +60 -0
  11. lionagi/ln/_fuzzy_json.py +116 -0
  12. lionagi/ln/_json_dump.py +75 -0
  13. lionagi/ln/_models.py +0 -1
  14. lionagi/models/field_model.py +8 -6
  15. lionagi/operations/__init__.py +3 -0
  16. lionagi/operations/builder.py +10 -0
  17. lionagi/protocols/generic/element.py +56 -53
  18. lionagi/protocols/generic/event.py +46 -67
  19. lionagi/protocols/generic/pile.py +56 -1
  20. lionagi/protocols/generic/progression.py +11 -11
  21. lionagi/protocols/graph/_utils.py +22 -0
  22. lionagi/protocols/graph/graph.py +17 -21
  23. lionagi/protocols/graph/node.py +23 -5
  24. lionagi/protocols/messages/manager.py +41 -45
  25. lionagi/protocols/messages/message.py +3 -1
  26. lionagi/protocols/operatives/step.py +2 -19
  27. lionagi/protocols/types.py +1 -2
  28. lionagi/service/connections/providers/claude_code_.py +9 -7
  29. lionagi/service/third_party/claude_code.py +3 -2
  30. lionagi/session/session.py +14 -2
  31. lionagi/tools/file/reader.py +5 -6
  32. lionagi/utils.py +8 -385
  33. lionagi/version.py +1 -1
  34. {lionagi-0.15.8.dist-info → lionagi-0.15.11.dist-info}/METADATA +2 -2
  35. {lionagi-0.15.8.dist-info → lionagi-0.15.11.dist-info}/RECORD +37 -37
  36. lionagi/libs/package/__init__.py +0 -3
  37. lionagi/libs/package/imports.py +0 -21
  38. lionagi/libs/package/management.py +0 -62
  39. lionagi/libs/package/params.py +0 -30
  40. lionagi/libs/package/system.py +0 -22
  41. {lionagi-0.15.8.dist-info → lionagi-0.15.11.dist-info}/WHEEL +0 -0
  42. {lionagi-0.15.8.dist-info → lionagi-0.15.11.dist-info}/licenses/LICENSE +0 -0
@@ -2,36 +2,19 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
- from pydantic import BaseModel, Field
5
+ from pydantic import BaseModel
6
6
  from pydantic.fields import FieldInfo
7
7
 
8
8
  from lionagi.fields.action import (
9
9
  ACTION_REQUESTS_FIELD,
10
10
  ACTION_REQUIRED_FIELD,
11
11
  ACTION_RESPONSES_FIELD,
12
- ActionRequestModel,
13
- ActionResponseModel,
14
12
  )
15
- from lionagi.fields.reason import REASON_FIELD, Reason
13
+ from lionagi.fields.reason import REASON_FIELD
16
14
  from lionagi.models import FieldModel, ModelParams
17
15
  from lionagi.protocols.operatives.operative import Operative
18
16
 
19
17
 
20
- class StepModel(BaseModel):
21
- """Model representing a single operational step with optional reasoning and actions."""
22
-
23
- title: str
24
- description: str
25
- reason: Reason | None = Field(**REASON_FIELD.to_dict())
26
- action_requests: list[ActionRequestModel] = Field(
27
- **ACTION_REQUESTS_FIELD.to_dict()
28
- )
29
- action_required: bool = Field(**ACTION_REQUIRED_FIELD.to_dict())
30
- action_responses: list[ActionResponseModel] = Field(
31
- **ACTION_RESPONSES_FIELD.to_dict()
32
- )
33
-
34
-
35
18
  class Step:
36
19
  """Utility class providing methods to create and manage Operative instances for steps."""
37
20
 
@@ -49,7 +49,7 @@ from .messages.manager import (
49
49
  SenderRecipient,
50
50
  System,
51
51
  )
52
- from .operatives.step import Operative, Step, StepModel
52
+ from .operatives.step import Operative, Step
53
53
 
54
54
  __all__ = (
55
55
  "Collective",
@@ -107,7 +107,6 @@ __all__ = (
107
107
  "Report",
108
108
  "Operative",
109
109
  "Step",
110
- "StepModel",
111
110
  "ActionManager",
112
111
  "Tool",
113
112
  "FunctionCalling",
@@ -4,11 +4,11 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- import json
8
7
  import warnings
9
8
 
10
9
  from pydantic import BaseModel
11
10
 
11
+ from lionagi import ln
12
12
  from lionagi.libs.schema.as_readable import as_readable
13
13
  from lionagi.service.connections.endpoint import Endpoint
14
14
  from lionagi.service.connections.endpoint_config import EndpointConfig
@@ -264,12 +264,14 @@ def _verbose_output(res) -> str:
264
264
  str_ += f"Claude:\n{text}"
265
265
 
266
266
  if isinstance(block, cc_types.ToolUseBlock):
267
- input = (
268
- json.dumps(block.input, indent=2)
269
- if isinstance(block.input, dict)
270
- else str(block.input)
271
- )
272
- input = input[:200] + "..." if len(input) > 200 else input
267
+ inp_ = None
268
+
269
+ if isinstance(block.input, dict | list):
270
+ inp_ = ln.json_dumps(block.input)
271
+ else:
272
+ inp_ = str(block.input)
273
+
274
+ input = inp_[:200] + "..." if len(inp_) > 200 else inp_
273
275
  str_ += (
274
276
  f"Tool Use: {block.name} - {block.id}\n - Input: {input}"
275
277
  )
@@ -22,6 +22,7 @@ from typing import Any, Literal
22
22
  from json_repair import repair_json
23
23
  from pydantic import BaseModel, Field, field_validator, model_validator
24
24
 
25
+ from lionagi import ln
25
26
  from lionagi.libs.schema.as_readable import as_readable
26
27
  from lionagi.utils import is_coro_func, is_import_installed
27
28
 
@@ -238,7 +239,7 @@ class ClaudeCodeRequest(BaseModel):
238
239
  continue_conversation = True
239
240
  prompt = messages[-1]["content"]
240
241
  if isinstance(prompt, (dict, list)):
241
- prompt = json.dumps(prompt)
242
+ prompt = ln.json_dumps(prompt)
242
243
 
243
244
  # 2. else, use entire messages except system message
244
245
  else:
@@ -248,7 +249,7 @@ class ClaudeCodeRequest(BaseModel):
248
249
  if message["role"] != "system":
249
250
  content = message["content"]
250
251
  prompts.append(
251
- json.dumps(content)
252
+ ln.json_dumps(content)
252
253
  if isinstance(content, (dict, list))
253
254
  else content
254
255
  )
@@ -7,7 +7,13 @@ from collections.abc import Callable
7
7
  from typing import Any
8
8
 
9
9
  import pandas as pd
10
- from pydantic import Field, JsonValue, PrivateAttr, model_validator
10
+ from pydantic import (
11
+ Field,
12
+ JsonValue,
13
+ PrivateAttr,
14
+ field_serializer,
15
+ model_validator,
16
+ )
11
17
  from typing_extensions import Self
12
18
 
13
19
  from lionagi.protocols.types import (
@@ -51,7 +57,7 @@ class Session(Node, Communicatable, Relational):
51
57
  default_factory=lambda: Pile(item_type={Branch}, strict_type=False)
52
58
  )
53
59
  default_branch: Any = Field(default=None, exclude=True)
54
- mail_transfer: Exchange = Field(default_factory=Exchange)
60
+ mail_transfer: Exchange = Field(default_factory=Exchange, exclude=True)
55
61
  mail_manager: MailManager = Field(
56
62
  default_factory=MailManager, exclude=True
57
63
  )
@@ -61,6 +67,12 @@ class Session(Node, Communicatable, Relational):
61
67
  default_factory=OperationManager
62
68
  )
63
69
 
70
+ @field_serializer("user")
71
+ def _serialize_user(self, value: SenderRecipient | None) -> JsonValue:
72
+ if value is None:
73
+ return None
74
+ return str(value)
75
+
64
76
  async def ainclude_branches(self, branches: ID[Branch].ItemSeq):
65
77
  async with self.branches:
66
78
  self.include_branches(branches)
@@ -173,13 +173,12 @@ class ReaderTool(LionTool):
173
173
  system_tool_name = "reader_tool"
174
174
 
175
175
  def __init__(self):
176
- from lionagi.libs.package.imports import check_import
176
+ from lionagi.libs.file.process import _HAS_DOCLING
177
177
 
178
- DocumentConverter = check_import(
179
- "docling",
180
- module_name="document_converter",
181
- import_name="DocumentConverter",
182
- )
178
+ if _HAS_DOCLING is not True:
179
+ raise _HAS_DOCLING
180
+
181
+ from docling.document_converter import DocumentConverter
183
182
 
184
183
  super().__init__()
185
184
  self.converter = DocumentConverter()
lionagi/utils.py CHANGED
@@ -2,18 +2,12 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
- import asyncio
6
5
  import contextlib
7
6
  import copy as _copy
8
7
  import dataclasses
9
- import functools
10
8
  import importlib.util
11
9
  import json
12
10
  import logging
13
- import re
14
- import shutil
15
- import subprocess
16
- import sys
17
11
  import types
18
12
  import uuid
19
13
  from collections.abc import (
@@ -23,7 +17,6 @@ from collections.abc import (
23
17
  Mapping,
24
18
  Sequence,
25
19
  )
26
- from concurrent.futures import ThreadPoolExecutor
27
20
  from datetime import datetime, timezone
28
21
  from enum import Enum as _Enum
29
22
  from functools import partial
@@ -44,16 +37,10 @@ from pydantic_core import PydanticUndefinedType
44
37
  from typing_extensions import deprecated
45
38
 
46
39
  from .libs.validate.xml_parser import xml_to_dict
47
- from .ln import (
48
- DataClass,
49
- Enum,
50
- KeysDict,
51
- Params,
52
- Undefined,
53
- UndefinedType,
54
- hash_dict,
55
- to_list,
56
- )
40
+ from .ln import DataClass, Enum, KeysDict, Params, Undefined, UndefinedType
41
+ from .ln import extract_json as to_json
42
+ from .ln import fuzzy_json as fuzzy_parse_json
43
+ from .ln import hash_dict, to_list
57
44
  from .ln.concurrency import is_coro_func
58
45
  from .settings import Settings
59
46
 
@@ -99,6 +86,7 @@ __all__ = (
99
86
  "hash_dict",
100
87
  "is_union_type",
101
88
  "union_members",
89
+ "to_json",
102
90
  )
103
91
 
104
92
 
@@ -313,6 +301,9 @@ def lcall(
313
301
  )
314
302
 
315
303
 
304
+ @deprecated(
305
+ "Use `lionagi.ln.alcall` instead, function signature has changed, this will be removed in future versions."
306
+ )
316
307
  async def alcall(
317
308
  input_: list[Any],
318
309
  func: Callable[..., T],
@@ -514,119 +505,6 @@ def create_path(
514
505
  return full_path
515
506
 
516
507
 
517
- def fuzzy_parse_json(
518
- str_to_parse: str, /
519
- ) -> dict[str, Any] | list[dict[str, Any]]:
520
- """
521
- Attempt to parse a JSON string, trying a few minimal "fuzzy" fixes if needed.
522
-
523
- Steps:
524
- 1. Parse directly with json.loads.
525
- 2. Replace single quotes with double quotes, normalize spacing, and try again.
526
- 3. Attempt to fix unmatched brackets using fix_json_string.
527
- 4. If all fail, raise ValueError.
528
-
529
- Args:
530
- str_to_parse: The JSON string to parse
531
-
532
- Returns:
533
- Parsed JSON (dict or list of dicts)
534
-
535
- Raises:
536
- ValueError: If the string cannot be parsed as valid JSON
537
- TypeError: If the input is not a string
538
- """
539
- _check_valid_str(str_to_parse)
540
-
541
- # 1. Direct attempt
542
- with contextlib.suppress(Exception):
543
- return json.loads(str_to_parse)
544
-
545
- # 2. Try cleaning: replace single quotes with double and normalize
546
- cleaned = _clean_json_string(str_to_parse.replace("'", '"'))
547
- with contextlib.suppress(Exception):
548
- return json.loads(cleaned)
549
-
550
- # 3. Try fixing brackets
551
- fixed = fix_json_string(cleaned)
552
- with contextlib.suppress(Exception):
553
- return json.loads(fixed)
554
-
555
- # If all attempts fail
556
- raise ValueError("Invalid JSON string")
557
-
558
-
559
- def _check_valid_str(str_to_parse: str, /):
560
- if not isinstance(str_to_parse, str):
561
- raise TypeError("Input must be a string")
562
- if not str_to_parse.strip():
563
- raise ValueError("Input string is empty")
564
-
565
-
566
- def _clean_json_string(s: str) -> str:
567
- """Basic normalization: replace unescaped single quotes, trim spaces, ensure keys are quoted."""
568
- # Replace unescaped single quotes with double quotes
569
- # '(?<!\\)'" means a single quote not preceded by a backslash
570
- s = re.sub(r"(?<!\\)'", '"', s)
571
- # Collapse multiple whitespaces
572
- s = re.sub(r"\s+", " ", s)
573
- # Ensure keys are quoted
574
- # This attempts to find patterns like { key: value } and turn them into {"key": value}
575
- s = re.sub(r'([{,])\s*([^"\s]+)\s*:', r'\1"\2":', s)
576
- return s.strip()
577
-
578
-
579
- def fix_json_string(str_to_parse: str, /) -> str:
580
- """Try to fix JSON string by ensuring brackets are matched properly."""
581
- if not str_to_parse:
582
- raise ValueError("Input string is empty")
583
-
584
- brackets = {"{": "}", "[": "]"}
585
- open_brackets = []
586
- pos = 0
587
- length = len(str_to_parse)
588
-
589
- while pos < length:
590
- char = str_to_parse[pos]
591
-
592
- if char == "\\":
593
- pos += 2 # Skip escaped chars
594
- continue
595
-
596
- if char == '"':
597
- pos += 1
598
- # skip string content
599
- while pos < length:
600
- if str_to_parse[pos] == "\\":
601
- pos += 2
602
- continue
603
- if str_to_parse[pos] == '"':
604
- pos += 1
605
- break
606
- pos += 1
607
- continue
608
-
609
- if char in brackets:
610
- open_brackets.append(brackets[char])
611
- elif char in brackets.values():
612
- if not open_brackets:
613
- # Extra closing bracket
614
- # Better to raise error than guess
615
- raise ValueError("Extra closing bracket found.")
616
- if open_brackets[-1] != char:
617
- # Mismatched bracket
618
- raise ValueError("Mismatched brackets.")
619
- open_brackets.pop()
620
-
621
- pos += 1
622
-
623
- # Add missing closing brackets if any
624
- if open_brackets:
625
- str_to_parse += "".join(reversed(open_brackets))
626
-
627
- return str_to_parse
628
-
629
-
630
508
  def to_dict(
631
509
  input_: Any,
632
510
  /,
@@ -947,63 +825,6 @@ def _to_dict(
947
825
  return dict(input_)
948
826
 
949
827
 
950
- # Precompile the regex for extracting JSON code blocks
951
- _JSON_BLOCK_PATTERN = re.compile(r"```json\s*(.*?)\s*```", re.DOTALL)
952
-
953
-
954
- def to_json(
955
- input_data: str | list[str], /, *, fuzzy_parse: bool = False
956
- ) -> dict[str, Any] | list[dict[str, Any]]:
957
- """
958
- Extract and parse JSON content from a string or markdown code blocks.
959
-
960
- Attempts direct JSON parsing first. If that fails, looks for JSON content
961
- within markdown code blocks denoted by ```json.
962
-
963
- Args:
964
- input_data (str | list[str]): The input string or list of strings to parse.
965
- fuzzy_parse (bool): If True, attempts fuzzy JSON parsing on failed attempts.
966
-
967
- Returns:
968
- dict or list of dicts:
969
- - If a single JSON object is found: returns a dict.
970
- - If multiple JSON objects are found: returns a list of dicts.
971
- - If no valid JSON found: returns an empty list.
972
- """
973
-
974
- # If input_data is a list, join into a single string
975
- if isinstance(input_data, list):
976
- input_str = "\n".join(input_data)
977
- else:
978
- input_str = input_data
979
-
980
- # 1. Try direct parsing
981
- try:
982
- if fuzzy_parse:
983
- return fuzzy_parse_json(input_str)
984
- return json.loads(input_str)
985
- except Exception:
986
- pass
987
-
988
- # 2. Attempt extracting JSON blocks from markdown
989
- matches = _JSON_BLOCK_PATTERN.findall(input_str)
990
- if not matches:
991
- return []
992
-
993
- # If only one match, return single dict; if multiple, return list of dicts
994
- if len(matches) == 1:
995
- data_str = matches[0]
996
- return (
997
- fuzzy_parse_json(data_str) if fuzzy_parse else json.loads(data_str)
998
- )
999
-
1000
- # Multiple matches
1001
- if fuzzy_parse:
1002
- return [fuzzy_parse_json(m) for m in matches]
1003
- else:
1004
- return [json.loads(m) for m in matches]
1005
-
1006
-
1007
828
  def get_bins(input_: list[str], upper: int) -> list[list[int]]:
1008
829
  """Organizes indices of strings into bins based on a cumulative upper limit.
1009
830
 
@@ -1030,79 +851,6 @@ def get_bins(input_: list[str], upper: int) -> list[list[int]]:
1030
851
  return bins
1031
852
 
1032
853
 
1033
- def force_async(fn: Callable[..., T]) -> Callable[..., Callable[..., T]]:
1034
- """
1035
- Convert a synchronous function to an asynchronous function
1036
- using a thread pool.
1037
-
1038
- Args:
1039
- fn: The synchronous function to convert.
1040
-
1041
- Returns:
1042
- The asynchronous version of the function.
1043
- """
1044
- pool = ThreadPoolExecutor()
1045
-
1046
- @functools.wraps(fn)
1047
- def wrapper(*args, **kwargs):
1048
- future = pool.submit(fn, *args, **kwargs)
1049
- return asyncio.wrap_future(future) # Make it awaitable
1050
-
1051
- return wrapper
1052
-
1053
-
1054
- def throttle(
1055
- func: Callable[..., T], period: float
1056
- ) -> Callable[..., Callable[..., T]]:
1057
- """
1058
- Throttle function execution to limit the rate of calls.
1059
-
1060
- Args:
1061
- func: The function to throttle.
1062
- period: The minimum time interval between consecutive calls.
1063
-
1064
- Returns:
1065
- The throttled function.
1066
- """
1067
- from .ln.concurrency.throttle import Throttle
1068
-
1069
- if not is_coro_func(func):
1070
- func = force_async(func)
1071
- throttle_instance = Throttle(period)
1072
-
1073
- @functools.wraps(func)
1074
- async def wrapper(*args, **kwargs):
1075
- await throttle_instance(func)(*args, **kwargs)
1076
- return await func(*args, **kwargs)
1077
-
1078
- return wrapper
1079
-
1080
-
1081
- def max_concurrent(
1082
- func: Callable[..., T], limit: int
1083
- ) -> Callable[..., Callable[..., T]]:
1084
- """
1085
- Limit the concurrency of function execution using a semaphore.
1086
-
1087
- Args:
1088
- func: The function to limit concurrency for.
1089
- limit: The maximum number of concurrent executions.
1090
-
1091
- Returns:
1092
- The function wrapped with concurrency control.
1093
- """
1094
- if not is_coro_func(func):
1095
- func = force_async(func)
1096
- semaphore = asyncio.Semaphore(limit)
1097
-
1098
- @functools.wraps(func)
1099
- async def wrapper(*args, **kwargs):
1100
- async with semaphore:
1101
- return await func(*args, **kwargs)
1102
-
1103
- return wrapper
1104
-
1105
-
1106
854
  def breakdown_pydantic_annotation(
1107
855
  model: type[B], max_depth: int | None = None, current_depth: int = 0
1108
856
  ) -> dict[str, Any]:
@@ -1142,87 +890,6 @@ def _is_pydantic_model(x: Any) -> bool:
1142
890
  return False
1143
891
 
1144
892
 
1145
- def run_package_manager_command(
1146
- args: Sequence[str],
1147
- ) -> subprocess.CompletedProcess[bytes]:
1148
- """Run a package manager command, using uv if available, otherwise falling back to pip."""
1149
- # Check if uv is available in PATH
1150
- uv_path = shutil.which("uv")
1151
-
1152
- if uv_path:
1153
- # Use uv if available
1154
- try:
1155
- return subprocess.run(
1156
- [uv_path] + list(args),
1157
- check=True,
1158
- capture_output=True,
1159
- )
1160
- except subprocess.CalledProcessError:
1161
- # If uv fails, fall back to pip
1162
- print("uv command failed, falling back to pip...")
1163
-
1164
- # Fall back to pip
1165
- return subprocess.run(
1166
- [sys.executable, "-m", "pip"] + list(args),
1167
- check=True,
1168
- capture_output=True,
1169
- )
1170
-
1171
-
1172
- def check_import(
1173
- package_name: str,
1174
- module_name: str | None = None,
1175
- import_name: str | None = None,
1176
- pip_name: str | None = None,
1177
- attempt_install: bool = True,
1178
- error_message: str = "",
1179
- ):
1180
- """
1181
- Check if a package is installed, attempt to install if not.
1182
-
1183
- Args:
1184
- package_name: The name of the package to check.
1185
- module_name: The specific module to import (if any).
1186
- import_name: The specific name to import from the module (if any).
1187
- pip_name: The name to use for pip installation (if different).
1188
- attempt_install: Whether to attempt installation if not found.
1189
- error_message: Custom error message to use if package not found.
1190
-
1191
- Raises:
1192
- ImportError: If the package is not found and not installed.
1193
- ValueError: If the import fails after installation attempt.
1194
- """
1195
- if not is_import_installed(package_name):
1196
- if attempt_install:
1197
- logging.info(
1198
- f"Package {package_name} not found. Attempting to install.",
1199
- )
1200
- try:
1201
- return install_import(
1202
- package_name=package_name,
1203
- module_name=module_name,
1204
- import_name=import_name,
1205
- pip_name=pip_name,
1206
- )
1207
- except ImportError as e:
1208
- raise ValueError(
1209
- f"Failed to install {package_name}: {e}"
1210
- ) from e
1211
- else:
1212
- logging.info(
1213
- f"Package {package_name} not found. {error_message}",
1214
- )
1215
- raise ImportError(
1216
- f"Package {package_name} not found. {error_message}",
1217
- )
1218
-
1219
- return import_module(
1220
- package_name=package_name,
1221
- module_name=module_name,
1222
- import_name=import_name,
1223
- )
1224
-
1225
-
1226
893
  def import_module(
1227
894
  package_name: str,
1228
895
  module_name: str = None,
@@ -1267,50 +934,6 @@ def import_module(
1267
934
  ) from e
1268
935
 
1269
936
 
1270
- def install_import(
1271
- package_name: str,
1272
- module_name: str | None = None,
1273
- import_name: str | None = None,
1274
- pip_name: str | None = None,
1275
- ):
1276
- """
1277
- Attempt to import a package, installing it if not found.
1278
-
1279
- Args:
1280
- package_name: The name of the package to import.
1281
- module_name: The specific module to import (if any).
1282
- import_name: The specific name to import from the module (if any).
1283
- pip_name: The name to use for pip installation (if different).
1284
-
1285
- Raises:
1286
- ImportError: If the package cannot be imported or installed.
1287
- subprocess.CalledProcessError: If pip installation fails.
1288
- """
1289
- pip_name = pip_name or package_name
1290
-
1291
- try:
1292
- return import_module(
1293
- package_name=package_name,
1294
- module_name=module_name,
1295
- import_name=import_name,
1296
- )
1297
- except ImportError:
1298
- logging.info(f"Installing {pip_name}...")
1299
- try:
1300
- run_package_manager_command(["install", pip_name])
1301
- return import_module(
1302
- package_name=package_name,
1303
- module_name=module_name,
1304
- import_name=import_name,
1305
- )
1306
- except subprocess.CalledProcessError as e:
1307
- raise ImportError(f"Failed to install {pip_name}: {e}") from e
1308
- except ImportError as e:
1309
- raise ImportError(
1310
- f"Failed to import {pip_name} after installation: {e}"
1311
- ) from e
1312
-
1313
-
1314
937
  def is_import_installed(package_name: str) -> bool:
1315
938
  """
1316
939
  Check if a package is installed.
lionagi/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.15.8"
1
+ __version__ = "0.15.11"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lionagi
3
- Version: 0.15.8
3
+ Version: 0.15.11
4
4
  Summary: An Intelligence Operating System.
5
5
  Author-email: HaiyangLi <quantocean.li@gmail.com>
6
6
  License: Apache License
@@ -228,7 +228,7 @@ Requires-Dist: json-repair>=0.40.0
228
228
  Requires-Dist: pillow>=11.0.0
229
229
  Requires-Dist: psutil>=6.0.0
230
230
  Requires-Dist: pydantic-settings>=2.8.0
231
- Requires-Dist: pydapter[pandas]>=1.0.0
231
+ Requires-Dist: pydapter[pandas]>=1.0.2
232
232
  Requires-Dist: python-dotenv>=1.1.0
233
233
  Requires-Dist: tiktoken>=0.9.0
234
234
  Requires-Dist: toml>=0.8.0