lionagi 0.16.2__py3-none-any.whl → 0.17.0__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/adapters/_utils.py +10 -23
- lionagi/adapters/async_postgres_adapter.py +83 -79
- lionagi/ln/__init__.py +4 -4
- lionagi/ln/_json_dump.py +0 -6
- lionagi/ln/fuzzy/__init__.py +4 -1
- lionagi/ln/fuzzy/_fuzzy_validate.py +109 -0
- lionagi/ln/fuzzy/_to_dict.py +388 -0
- lionagi/models/__init__.py +0 -2
- lionagi/operations/__init__.py +0 -6
- lionagi/operations/_visualize_graph.py +285 -0
- lionagi/operations/brainstorm/brainstorm.py +14 -12
- lionagi/operations/builder.py +23 -302
- lionagi/operations/communicate/communicate.py +1 -1
- lionagi/operations/flow.py +14 -11
- lionagi/operations/node.py +14 -3
- lionagi/operations/operate/operate.py +5 -11
- lionagi/operations/parse/parse.py +2 -3
- lionagi/operations/types.py +0 -2
- lionagi/operations/utils.py +11 -5
- lionagi/protocols/generic/pile.py +3 -7
- lionagi/protocols/graph/graph.py +23 -6
- lionagi/protocols/graph/node.py +0 -2
- lionagi/protocols/messages/message.py +0 -1
- lionagi/protocols/operatives/operative.py +2 -2
- lionagi/protocols/types.py +0 -15
- lionagi/service/connections/endpoint.py +11 -5
- lionagi/service/connections/match_endpoint.py +2 -10
- lionagi/service/connections/providers/types.py +1 -3
- lionagi/service/hooks/hook_event.py +1 -1
- lionagi/service/hooks/hook_registry.py +1 -1
- lionagi/service/rate_limited_processor.py +1 -1
- lionagi/session/branch.py +24 -18
- lionagi/session/session.py +2 -18
- lionagi/utils.py +3 -335
- lionagi/version.py +1 -1
- {lionagi-0.16.2.dist-info → lionagi-0.17.0.dist-info}/METADATA +4 -13
- {lionagi-0.16.2.dist-info → lionagi-0.17.0.dist-info}/RECORD +39 -61
- lionagi/adapters/postgres_model_adapter.py +0 -131
- lionagi/libs/concurrency.py +0 -1
- lionagi/libs/nested/__init__.py +0 -3
- lionagi/libs/nested/flatten.py +0 -172
- lionagi/libs/nested/nfilter.py +0 -59
- lionagi/libs/nested/nget.py +0 -45
- lionagi/libs/nested/ninsert.py +0 -104
- lionagi/libs/nested/nmerge.py +0 -158
- lionagi/libs/nested/npop.py +0 -69
- lionagi/libs/nested/nset.py +0 -94
- lionagi/libs/nested/unflatten.py +0 -83
- lionagi/libs/nested/utils.py +0 -189
- lionagi/libs/parse.py +0 -31
- lionagi/libs/schema/json_schema.py +0 -231
- lionagi/libs/unstructured/__init__.py +0 -0
- lionagi/libs/unstructured/pdf_to_image.py +0 -45
- lionagi/libs/unstructured/read_image_to_base64.py +0 -33
- lionagi/libs/validate/fuzzy_match_keys.py +0 -7
- lionagi/libs/validate/fuzzy_validate_mapping.py +0 -144
- lionagi/libs/validate/string_similarity.py +0 -7
- lionagi/libs/validate/xml_parser.py +0 -203
- lionagi/models/note.py +0 -387
- lionagi/protocols/graph/_utils.py +0 -22
- lionagi/service/connections/providers/claude_code_.py +0 -299
- {lionagi-0.16.2.dist-info → lionagi-0.17.0.dist-info}/WHEEL +0 -0
- {lionagi-0.16.2.dist-info → lionagi-0.17.0.dist-info}/licenses/LICENSE +0 -0
lionagi/adapters/_utils.py
CHANGED
@@ -1,26 +1,13 @@
|
|
1
1
|
def check_async_postgres_available():
|
2
|
-
|
3
|
-
import sqlalchemy as sa
|
4
|
-
from pydapter.extras.async_postgres_ import AsyncPostgresAdapter
|
5
|
-
from sqlalchemy.ext.asyncio import create_async_engine
|
2
|
+
from lionagi.utils import is_import_installed
|
6
3
|
|
4
|
+
all_import_present = 0
|
5
|
+
for pkg in ("sqlalchemy", "asyncpg"):
|
6
|
+
if is_import_installed(pkg):
|
7
|
+
all_import_present += 1
|
8
|
+
if all_import_present == 2:
|
7
9
|
return True
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
)
|
13
|
-
|
14
|
-
|
15
|
-
def check_postgres_available():
|
16
|
-
try:
|
17
|
-
from pydapter.model_adapters.postgres_model import PostgresModelAdapter
|
18
|
-
from sqlalchemy import String
|
19
|
-
from sqlalchemy.orm import DeclarativeBase
|
20
|
-
|
21
|
-
return True
|
22
|
-
except Exception:
|
23
|
-
return ImportError(
|
24
|
-
"This adapter requires postgres option to be installed. "
|
25
|
-
'Please install them using `uv pip install "lionagi[postgres]"`.'
|
26
|
-
)
|
10
|
+
return ImportError(
|
11
|
+
"This adapter requires postgres option to be installed. "
|
12
|
+
'Please install them using `uv pip install "lionagi[postgres]"`.'
|
13
|
+
)
|
@@ -13,86 +13,90 @@ from __future__ import annotations
|
|
13
13
|
|
14
14
|
from typing import ClassVar, TypeVar
|
15
15
|
|
16
|
-
|
17
|
-
from pydapter.extras.async_postgres_ import AsyncPostgresAdapter
|
18
|
-
from sqlalchemy.ext.asyncio import create_async_engine
|
19
|
-
|
20
|
-
from ._utils import check_async_postgres_available
|
21
|
-
|
22
|
-
_ASYNC_POSTGRES_AVAILABLE = check_async_postgres_available()
|
23
|
-
|
24
|
-
if isinstance(_ASYNC_POSTGRES_AVAILABLE, ImportError):
|
25
|
-
raise _ASYNC_POSTGRES_AVAILABLE
|
16
|
+
from pydapter import AsyncAdapter
|
26
17
|
|
27
18
|
T = TypeVar("T")
|
28
19
|
|
29
20
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
sa.
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
21
|
+
def create_lionagi_async_postgres_adapter() -> type[AsyncAdapter]:
|
22
|
+
from pydapter.extras.async_postgres_ import AsyncPostgresAdapter
|
23
|
+
|
24
|
+
class LionAGIAsyncPostgresAdapter(AsyncPostgresAdapter[T]):
|
25
|
+
"""
|
26
|
+
Streamlined async adapter for lionagi Nodes.
|
27
|
+
|
28
|
+
Features:
|
29
|
+
- Auto-creates tables with lionagi schema
|
30
|
+
- Inherits all pydapter v1.0.4+ improvements
|
31
|
+
- No workarounds needed for SQLite or raw SQL
|
32
|
+
"""
|
33
|
+
|
34
|
+
obj_key: ClassVar[str] = "lionagi_async_pg"
|
35
|
+
|
36
|
+
@classmethod
|
37
|
+
async def to_obj(
|
38
|
+
cls,
|
39
|
+
subj,
|
40
|
+
/,
|
41
|
+
*,
|
42
|
+
many: bool = True,
|
43
|
+
adapt_meth: str = None,
|
44
|
+
**kw,
|
45
|
+
):
|
46
|
+
"""Write lionagi Node(s) to database with auto-table creation."""
|
47
|
+
# Auto-create table if needed
|
48
|
+
if table := kw.get("table"):
|
49
|
+
if engine_url := (kw.get("dsn") or kw.get("engine_url")):
|
50
|
+
await cls._ensure_table(engine_url, table)
|
51
|
+
elif engine := kw.get("engine"):
|
52
|
+
await cls._ensure_table(engine, table)
|
53
|
+
|
54
|
+
return await super().to_obj(
|
55
|
+
subj, many=many, adapt_meth=adapt_meth, **kw
|
56
|
+
)
|
57
|
+
|
58
|
+
@classmethod
|
59
|
+
async def _ensure_table(cls, engine_or_url, table_name: str):
|
60
|
+
"""Create table with lionagi schema if it doesn't exist."""
|
61
|
+
import sqlalchemy as sa
|
62
|
+
from sqlalchemy.ext.asyncio import create_async_engine
|
63
|
+
|
64
|
+
should_dispose = False
|
65
|
+
if isinstance(engine_or_url, str):
|
66
|
+
engine = create_async_engine(engine_or_url, future=True)
|
67
|
+
should_dispose = True
|
68
|
+
else:
|
69
|
+
engine = engine_or_url
|
70
|
+
|
71
|
+
try:
|
72
|
+
async with engine.begin() as conn:
|
73
|
+
# Determine JSON type based on database
|
74
|
+
engine_url = str(engine.url)
|
75
|
+
json_type = (
|
76
|
+
sa.dialects.postgresql.JSONB
|
77
|
+
if "postgresql" in engine_url
|
78
|
+
else sa.JSON
|
79
|
+
)
|
80
|
+
|
81
|
+
# Create table with lionagi schema
|
82
|
+
await conn.run_sync(
|
83
|
+
lambda sync_conn: sa.Table(
|
84
|
+
table_name,
|
85
|
+
sa.MetaData(),
|
86
|
+
sa.Column("id", sa.String, primary_key=True),
|
87
|
+
sa.Column("content", json_type),
|
88
|
+
sa.Column("node_metadata", json_type),
|
89
|
+
sa.Column("created_at", sa.Float),
|
90
|
+
sa.Column("embedding", json_type, nullable=True),
|
91
|
+
).create(sync_conn, checkfirst=True)
|
92
|
+
)
|
93
|
+
finally:
|
94
|
+
if should_dispose:
|
95
|
+
await engine.dispose()
|
96
|
+
|
97
|
+
return LionAGIAsyncPostgresAdapter
|
98
|
+
|
99
|
+
|
100
|
+
LionAGIAsyncPostgresAdapter = create_lionagi_async_postgres_adapter()
|
101
|
+
|
102
|
+
__all__ = ("LionAGIAsyncPostgresAdapter",)
|
lionagi/ln/__init__.py
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
from ._async_call import alcall, bcall
|
2
2
|
from ._hash import hash_dict
|
3
3
|
from ._json_dump import (
|
4
|
-
DEFAULT_SERIALIZER,
|
5
|
-
DEFAULT_SERIALIZER_OPTION,
|
6
4
|
get_orjson_default,
|
7
5
|
json_dumpb,
|
8
6
|
json_dumps,
|
@@ -37,8 +35,10 @@ from .fuzzy import (
|
|
37
35
|
extract_json,
|
38
36
|
fuzzy_json,
|
39
37
|
fuzzy_match_keys,
|
38
|
+
fuzzy_validate_mapping,
|
40
39
|
fuzzy_validate_pydantic,
|
41
40
|
string_similarity,
|
41
|
+
to_dict,
|
42
42
|
)
|
43
43
|
from .types import is_sentinel, not_sentinel
|
44
44
|
|
@@ -46,8 +46,6 @@ __all__ = (
|
|
46
46
|
"alcall",
|
47
47
|
"bcall",
|
48
48
|
"hash_dict",
|
49
|
-
"DEFAULT_SERIALIZER",
|
50
|
-
"DEFAULT_SERIALIZER_OPTION",
|
51
49
|
"get_orjson_default",
|
52
50
|
"json_dumps",
|
53
51
|
"make_options",
|
@@ -80,4 +78,6 @@ __all__ = (
|
|
80
78
|
"string_similarity",
|
81
79
|
"is_sentinel",
|
82
80
|
"not_sentinel",
|
81
|
+
"to_dict",
|
82
|
+
"fuzzy_validate_mapping",
|
83
83
|
)
|
lionagi/ln/_json_dump.py
CHANGED
@@ -15,8 +15,6 @@ import orjson
|
|
15
15
|
|
16
16
|
__all__ = [
|
17
17
|
"get_orjson_default",
|
18
|
-
"DEFAULT_SERIALIZER",
|
19
|
-
"DEFAULT_SERIALIZER_OPTION",
|
20
18
|
"make_options",
|
21
19
|
"json_dumpb",
|
22
20
|
"json_dumps",
|
@@ -193,10 +191,6 @@ def _cached_default(
|
|
193
191
|
|
194
192
|
# --------- defaults & options -------------------------------------------------
|
195
193
|
|
196
|
-
# Compact, no newline, no sorting: neutral default for most use-cases.
|
197
|
-
DEFAULT_SERIALIZER_OPTION = 0
|
198
|
-
DEFAULT_SERIALIZER = get_orjson_default()
|
199
|
-
|
200
194
|
|
201
195
|
def make_options(
|
202
196
|
*,
|
lionagi/ln/fuzzy/__init__.py
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
from ._extract_json import extract_json
|
2
2
|
from ._fuzzy_json import fuzzy_json
|
3
3
|
from ._fuzzy_match import FuzzyMatchKeysParams, fuzzy_match_keys
|
4
|
-
from ._fuzzy_validate import fuzzy_validate_pydantic
|
4
|
+
from ._fuzzy_validate import fuzzy_validate_mapping, fuzzy_validate_pydantic
|
5
5
|
from ._string_similarity import SIMILARITY_TYPE, string_similarity
|
6
|
+
from ._to_dict import to_dict
|
6
7
|
|
7
8
|
__all__ = (
|
9
|
+
"to_dict",
|
8
10
|
"fuzzy_json",
|
9
11
|
"fuzzy_match_keys",
|
10
12
|
"extract_json",
|
@@ -12,4 +14,5 @@ __all__ = (
|
|
12
14
|
"SIMILARITY_TYPE",
|
13
15
|
"fuzzy_validate_pydantic",
|
14
16
|
"FuzzyMatchKeysParams",
|
17
|
+
"fuzzy_validate_mapping",
|
15
18
|
)
|
@@ -1,9 +1,15 @@
|
|
1
|
+
from collections.abc import Callable, Sequence
|
2
|
+
from typing import Any, Literal
|
3
|
+
|
1
4
|
from pydantic import BaseModel
|
2
5
|
|
3
6
|
from lionagi._errors import ValidationError
|
4
7
|
|
8
|
+
from ..types import KeysDict
|
5
9
|
from ._extract_json import extract_json
|
6
10
|
from ._fuzzy_match import FuzzyMatchKeysParams, fuzzy_match_keys
|
11
|
+
from ._string_similarity import SIMILARITY_TYPE
|
12
|
+
from ._to_dict import to_dict
|
7
13
|
|
8
14
|
__all__ = ("fuzzy_validate_pydantic",)
|
9
15
|
|
@@ -44,3 +50,106 @@ def fuzzy_validate_pydantic(
|
|
44
50
|
return model_type.model_validate(model_data)
|
45
51
|
except Exception as e:
|
46
52
|
raise ValidationError(f"Validation failed: {e}") from e
|
53
|
+
|
54
|
+
|
55
|
+
def fuzzy_validate_mapping(
|
56
|
+
d: Any,
|
57
|
+
keys: Sequence[str] | KeysDict,
|
58
|
+
/,
|
59
|
+
*,
|
60
|
+
similarity_algo: (
|
61
|
+
SIMILARITY_TYPE | Callable[[str, str], float]
|
62
|
+
) = "jaro_winkler",
|
63
|
+
similarity_threshold: float = 0.85,
|
64
|
+
fuzzy_match: bool = True,
|
65
|
+
handle_unmatched: Literal[
|
66
|
+
"ignore", "raise", "remove", "fill", "force"
|
67
|
+
] = "ignore",
|
68
|
+
fill_value: Any = None,
|
69
|
+
fill_mapping: dict[str, Any] | None = None,
|
70
|
+
strict: bool = False,
|
71
|
+
suppress_conversion_errors: bool = False,
|
72
|
+
) -> dict[str, Any]:
|
73
|
+
"""
|
74
|
+
Validate and correct any input into a dictionary with expected keys.
|
75
|
+
|
76
|
+
Args:
|
77
|
+
d: Input to validate. Can be:
|
78
|
+
- Dictionary
|
79
|
+
- JSON string or markdown code block
|
80
|
+
- XML string
|
81
|
+
- Object with to_dict/model_dump method
|
82
|
+
- Any type convertible to dictionary
|
83
|
+
keys: List of expected keys or dictionary mapping keys to types.
|
84
|
+
similarity_algo: String similarity algorithm or custom function.
|
85
|
+
similarity_threshold: Minimum similarity score for fuzzy matching.
|
86
|
+
fuzzy_match: If True, use fuzzy matching for key correction.
|
87
|
+
handle_unmatched: How to handle unmatched keys:
|
88
|
+
- "ignore": Keep unmatched keys
|
89
|
+
- "raise": Raise error for unmatched keys
|
90
|
+
- "remove": Remove unmatched keys
|
91
|
+
- "fill": Fill missing keys with default values
|
92
|
+
- "force": Combine "fill" and "remove" behaviors
|
93
|
+
fill_value: Default value for filling unmatched keys.
|
94
|
+
fill_mapping: Dictionary mapping keys to default values.
|
95
|
+
strict: Raise error if any expected key is missing.
|
96
|
+
suppress_conversion_errors: Return empty dict on conversion errors.
|
97
|
+
|
98
|
+
Returns:
|
99
|
+
Validated and corrected dictionary.
|
100
|
+
|
101
|
+
Raises:
|
102
|
+
ValueError: If input cannot be converted or validation fails.
|
103
|
+
TypeError: If input types are invalid.
|
104
|
+
"""
|
105
|
+
if d is None:
|
106
|
+
raise TypeError("Input cannot be None")
|
107
|
+
|
108
|
+
# Try converting to dictionary
|
109
|
+
try:
|
110
|
+
if isinstance(d, str):
|
111
|
+
# First try to_json for JSON strings and code blocks
|
112
|
+
try:
|
113
|
+
json_result = extract_json(
|
114
|
+
d, fuzzy_parse=True, return_one_if_single=True
|
115
|
+
)
|
116
|
+
dict_input = (
|
117
|
+
json_result[0]
|
118
|
+
if isinstance(json_result, list)
|
119
|
+
else json_result
|
120
|
+
)
|
121
|
+
except Exception:
|
122
|
+
dict_input = to_dict(
|
123
|
+
d, str_type="json", fuzzy_parse=True, suppress=True
|
124
|
+
)
|
125
|
+
else:
|
126
|
+
dict_input = to_dict(
|
127
|
+
d, use_model_dump=True, fuzzy_parse=True, suppress=True
|
128
|
+
)
|
129
|
+
|
130
|
+
if not isinstance(dict_input, dict):
|
131
|
+
if suppress_conversion_errors:
|
132
|
+
dict_input = {}
|
133
|
+
else:
|
134
|
+
raise ValueError(
|
135
|
+
f"Failed to convert input to dictionary: {type(dict_input)}"
|
136
|
+
)
|
137
|
+
|
138
|
+
except Exception as e:
|
139
|
+
if suppress_conversion_errors:
|
140
|
+
dict_input = {}
|
141
|
+
else:
|
142
|
+
raise ValueError(f"Failed to convert input to dictionary: {e}")
|
143
|
+
|
144
|
+
# Validate the dictionary
|
145
|
+
return fuzzy_match_keys(
|
146
|
+
dict_input,
|
147
|
+
keys,
|
148
|
+
similarity_algo=similarity_algo,
|
149
|
+
similarity_threshold=similarity_threshold,
|
150
|
+
fuzzy_match=fuzzy_match,
|
151
|
+
handle_unmatched=handle_unmatched,
|
152
|
+
fill_value=fill_value,
|
153
|
+
fill_mapping=fill_mapping,
|
154
|
+
strict=strict,
|
155
|
+
)
|