lionagi 0.18.0__py3-none-any.whl → 0.18.2__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/__init__.py +102 -59
 - lionagi/_errors.py +0 -5
 - lionagi/adapters/spec_adapters/__init__.py +9 -0
 - lionagi/adapters/spec_adapters/_protocol.py +236 -0
 - lionagi/adapters/spec_adapters/pydantic_field.py +158 -0
 - lionagi/fields.py +83 -0
 - lionagi/ln/__init__.py +3 -1
 - lionagi/ln/_async_call.py +2 -2
 - lionagi/ln/concurrency/primitives.py +4 -4
 - lionagi/ln/concurrency/task.py +1 -0
 - lionagi/ln/fuzzy/_fuzzy_match.py +2 -2
 - lionagi/ln/types/__init__.py +51 -0
 - lionagi/ln/types/_sentinel.py +154 -0
 - lionagi/ln/{types.py → types/base.py} +108 -168
 - lionagi/ln/types/operable.py +221 -0
 - lionagi/ln/types/spec.py +441 -0
 - lionagi/models/field_model.py +69 -7
 - lionagi/models/hashable_model.py +2 -3
 - lionagi/models/model_params.py +4 -3
 - lionagi/operations/ReAct/ReAct.py +1 -1
 - lionagi/operations/act/act.py +3 -3
 - lionagi/operations/builder.py +5 -7
 - lionagi/operations/fields.py +380 -0
 - lionagi/operations/flow.py +4 -6
 - lionagi/operations/node.py +4 -4
 - lionagi/operations/operate/operate.py +123 -89
 - lionagi/operations/operate/operative.py +198 -0
 - lionagi/operations/operate/step.py +203 -0
 - lionagi/operations/select/select.py +1 -1
 - lionagi/operations/select/utils.py +7 -1
 - lionagi/operations/types.py +7 -7
 - lionagi/protocols/action/manager.py +5 -6
 - lionagi/protocols/contracts.py +2 -2
 - lionagi/protocols/generic/__init__.py +22 -0
 - lionagi/protocols/generic/element.py +36 -127
 - lionagi/protocols/generic/pile.py +9 -10
 - lionagi/protocols/generic/progression.py +23 -22
 - lionagi/protocols/graph/edge.py +6 -5
 - lionagi/protocols/ids.py +6 -49
 - lionagi/protocols/messages/__init__.py +3 -1
 - lionagi/protocols/messages/base.py +7 -6
 - lionagi/protocols/messages/instruction.py +0 -1
 - lionagi/protocols/messages/message.py +2 -2
 - lionagi/protocols/types.py +1 -11
 - lionagi/service/connections/__init__.py +3 -0
 - lionagi/service/connections/providers/claude_code_cli.py +3 -2
 - lionagi/service/hooks/_types.py +1 -1
 - lionagi/service/hooks/_utils.py +1 -1
 - lionagi/service/hooks/hook_event.py +3 -8
 - lionagi/service/hooks/hook_registry.py +5 -5
 - lionagi/service/hooks/hooked_event.py +61 -1
 - lionagi/service/imodel.py +24 -20
 - lionagi/service/third_party/claude_code.py +1 -2
 - lionagi/service/third_party/openai_models.py +24 -22
 - lionagi/service/token_calculator.py +1 -94
 - lionagi/session/branch.py +26 -228
 - lionagi/session/session.py +5 -90
 - lionagi/version.py +1 -1
 - {lionagi-0.18.0.dist-info → lionagi-0.18.2.dist-info}/METADATA +6 -5
 - {lionagi-0.18.0.dist-info → lionagi-0.18.2.dist-info}/RECORD +62 -82
 - lionagi/fields/__init__.py +0 -47
 - lionagi/fields/action.py +0 -188
 - lionagi/fields/base.py +0 -153
 - lionagi/fields/code.py +0 -239
 - lionagi/fields/file.py +0 -234
 - lionagi/fields/instruct.py +0 -135
 - lionagi/fields/reason.py +0 -55
 - lionagi/fields/research.py +0 -52
 - lionagi/operations/brainstorm/__init__.py +0 -2
 - lionagi/operations/brainstorm/brainstorm.py +0 -498
 - lionagi/operations/brainstorm/prompt.py +0 -11
 - lionagi/operations/instruct/__init__.py +0 -2
 - lionagi/operations/instruct/instruct.py +0 -28
 - lionagi/operations/plan/__init__.py +0 -6
 - lionagi/operations/plan/plan.py +0 -386
 - lionagi/operations/plan/prompt.py +0 -25
 - lionagi/operations/utils.py +0 -45
 - lionagi/protocols/forms/__init__.py +0 -2
 - lionagi/protocols/forms/base.py +0 -85
 - lionagi/protocols/forms/flow.py +0 -79
 - lionagi/protocols/forms/form.py +0 -86
 - lionagi/protocols/forms/report.py +0 -48
 - lionagi/protocols/mail/__init__.py +0 -2
 - lionagi/protocols/mail/exchange.py +0 -220
 - lionagi/protocols/mail/mail.py +0 -51
 - lionagi/protocols/mail/mailbox.py +0 -103
 - lionagi/protocols/mail/manager.py +0 -218
 - lionagi/protocols/mail/package.py +0 -101
 - lionagi/protocols/operatives/__init__.py +0 -2
 - lionagi/protocols/operatives/operative.py +0 -362
 - lionagi/protocols/operatives/step.py +0 -227
 - {lionagi-0.18.0.dist-info → lionagi-0.18.2.dist-info}/WHEEL +0 -0
 - {lionagi-0.18.0.dist-info → lionagi-0.18.2.dist-info}/licenses/LICENSE +0 -0
 
| 
         @@ -3,152 +3,26 @@ from __future__ import annotations 
     | 
|
| 
       3 
3 
     | 
    
         
             
            from collections.abc import Sequence
         
     | 
| 
       4 
4 
     | 
    
         
             
            from dataclasses import dataclass, field
         
     | 
| 
       5 
5 
     | 
    
         
             
            from enum import Enum as _Enum
         
     | 
| 
       6 
     | 
    
         
            -
            from typing import Any, ClassVar 
     | 
| 
      
 6 
     | 
    
         
            +
            from typing import Any, ClassVar
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            from typing_extensions import TypedDict, override
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
      
 10 
     | 
    
         
            +
            from ._sentinel import Undefined, Unset, is_sentinel
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
       10 
12 
     | 
    
         
             
            __all__ = (
         
     | 
| 
       11 
     | 
    
         
            -
                "Undefined",
         
     | 
| 
       12 
     | 
    
         
            -
                "Unset",
         
     | 
| 
       13 
     | 
    
         
            -
                "MaybeUndefined",
         
     | 
| 
       14 
     | 
    
         
            -
                "MaybeUnset",
         
     | 
| 
       15 
     | 
    
         
            -
                "MaybeSentinel",
         
     | 
| 
       16 
     | 
    
         
            -
                "SingletonType",
         
     | 
| 
       17 
     | 
    
         
            -
                "UndefinedType",
         
     | 
| 
       18 
     | 
    
         
            -
                "UnsetType",
         
     | 
| 
       19 
     | 
    
         
            -
                "KeysDict",
         
     | 
| 
       20 
     | 
    
         
            -
                "T",
         
     | 
| 
       21 
13 
     | 
    
         
             
                "Enum",
         
     | 
| 
       22 
     | 
    
         
            -
                " 
     | 
| 
       23 
     | 
    
         
            -
                "not_sentinel",
         
     | 
| 
      
 14 
     | 
    
         
            +
                "ModelConfig",
         
     | 
| 
       24 
15 
     | 
    
         
             
                "Params",
         
     | 
| 
       25 
16 
     | 
    
         
             
                "DataClass",
         
     | 
| 
       26 
     | 
    
         
            -
                "KeysLike",
         
     | 
| 
       27 
17 
     | 
    
         
             
                "Meta",
         
     | 
| 
      
 18 
     | 
    
         
            +
                "KeysDict",
         
     | 
| 
      
 19 
     | 
    
         
            +
                "KeysLike",
         
     | 
| 
       28 
20 
     | 
    
         
             
            )
         
     | 
| 
       29 
21 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
            T = TypeVar("T")
         
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
            class _SingletonMeta(type):
         
     | 
| 
       34 
     | 
    
         
            -
                """Metaclass that guarantees exactly one instance per subclass.
         
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
                This ensures that sentinel values maintain identity across the entire application,
         
     | 
| 
       37 
     | 
    
         
            -
                allowing safe identity checks with 'is' operator.
         
     | 
| 
       38 
     | 
    
         
            -
                """
         
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
                _cache: dict[type, SingletonType] = {}
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
                def __call__(cls, *a, **kw):
         
     | 
| 
       43 
     | 
    
         
            -
                    if cls not in cls._cache:
         
     | 
| 
       44 
     | 
    
         
            -
                        cls._cache[cls] = super().__call__(*a, **kw)
         
     | 
| 
       45 
     | 
    
         
            -
                    return cls._cache[cls]
         
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
            class SingletonType(metaclass=_SingletonMeta):
         
     | 
| 
       49 
     | 
    
         
            -
                """Base class for singleton sentinel types.
         
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                Provides consistent interface for sentinel values with:
         
     | 
| 
       52 
     | 
    
         
            -
                - Identity preservation across deepcopy
         
     | 
| 
       53 
     | 
    
         
            -
                - Falsy boolean evaluation
         
     | 
| 
       54 
     | 
    
         
            -
                - Clear string representation
         
     | 
| 
       55 
     | 
    
         
            -
                """
         
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
       57 
     | 
    
         
            -
                __slots__: tuple[str, ...] = ()
         
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
                def __deepcopy__(self, memo):  # copy & deepcopy both noop
         
     | 
| 
       60 
     | 
    
         
            -
                    return self
         
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
                def __copy__(self):
         
     | 
| 
       63 
     | 
    
         
            -
                    return self
         
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                # concrete classes *must* override the two methods below
         
     | 
| 
       66 
     | 
    
         
            -
                def __bool__(self) -> bool: ...
         
     | 
| 
       67 
     | 
    
         
            -
                def __repr__(self) -> str: ...
         
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
            class UndefinedType(SingletonType):
         
     | 
| 
       71 
     | 
    
         
            -
                """Sentinel for a key or field entirely missing from a namespace.
         
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
                Use this when:
         
     | 
| 
       74 
     | 
    
         
            -
                - A field has never been set
         
     | 
| 
       75 
     | 
    
         
            -
                - A key doesn't exist in a mapping
         
     | 
| 
       76 
     | 
    
         
            -
                - A value is conceptually undefined (not just unset)
         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
                Example:
         
     | 
| 
       79 
     | 
    
         
            -
                    >>> d = {"a": 1}
         
     | 
| 
       80 
     | 
    
         
            -
                    >>> d.get("b", Undefined) is Undefined
         
     | 
| 
       81 
     | 
    
         
            -
                    True
         
     | 
| 
       82 
     | 
    
         
            -
                """
         
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
                __slots__ = ()
         
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
                def __bool__(self) -> Literal[False]:
         
     | 
| 
       87 
     | 
    
         
            -
                    return False
         
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
                def __repr__(self) -> Literal["Undefined"]:
         
     | 
| 
       90 
     | 
    
         
            -
                    return "Undefined"
         
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
                def __str__(self) -> Literal["Undefined"]:
         
     | 
| 
       93 
     | 
    
         
            -
                    return "Undefined"
         
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
                def __reduce__(self):
         
     | 
| 
       96 
     | 
    
         
            -
                    """Ensure pickle preservation of singleton identity."""
         
     | 
| 
       97 
     | 
    
         
            -
                    return "Undefined"
         
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
            class UnsetType(SingletonType):
         
     | 
| 
       101 
     | 
    
         
            -
                """Sentinel for a key present but value not yet provided.
         
     | 
| 
       102 
     | 
    
         
            -
             
     | 
| 
       103 
     | 
    
         
            -
                Use this when:
         
     | 
| 
       104 
     | 
    
         
            -
                - A parameter exists but hasn't been given a value
         
     | 
| 
       105 
     | 
    
         
            -
                - Distinguishing between None and "not provided"
         
     | 
| 
       106 
     | 
    
         
            -
                - API parameters that are optional but need explicit handling
         
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
       108 
     | 
    
         
            -
                Example:
         
     | 
| 
       109 
     | 
    
         
            -
                    >>> def func(param=Unset):
         
     | 
| 
       110 
     | 
    
         
            -
                    ...     if param is not Unset:
         
     | 
| 
       111 
     | 
    
         
            -
                    ...         # param was explicitly provided
         
     | 
| 
       112 
     | 
    
         
            -
                    ...         process(param)
         
     | 
| 
       113 
     | 
    
         
            -
                """
         
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
                __slots__ = ()
         
     | 
| 
       116 
     | 
    
         
            -
             
     | 
| 
       117 
     | 
    
         
            -
                def __bool__(self) -> Literal[False]:
         
     | 
| 
       118 
     | 
    
         
            -
                    return False
         
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
     | 
    
         
            -
                def __repr__(self) -> Literal["Unset"]:
         
     | 
| 
       121 
     | 
    
         
            -
                    return "Unset"
         
     | 
| 
       122 
     | 
    
         
            -
             
     | 
| 
       123 
     | 
    
         
            -
                def __str__(self) -> Literal["Unset"]:
         
     | 
| 
       124 
     | 
    
         
            -
                    return "Unset"
         
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
                def __reduce__(self):
         
     | 
| 
       127 
     | 
    
         
            -
                    """Ensure pickle preservation of singleton identity."""
         
     | 
| 
       128 
     | 
    
         
            -
                    return "Unset"
         
     | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
       131 
     | 
    
         
            -
            Undefined: Final = UndefinedType()
         
     | 
| 
       132 
     | 
    
         
            -
            """A key or field entirely missing from a namespace"""
         
     | 
| 
       133 
     | 
    
         
            -
            Unset: Final = UnsetType()
         
     | 
| 
       134 
     | 
    
         
            -
            """A key present but value not yet provided."""
         
     | 
| 
       135 
     | 
    
         
            -
             
     | 
| 
       136 
     | 
    
         
            -
            MaybeUndefined = Union[T, UndefinedType]
         
     | 
| 
       137 
     | 
    
         
            -
            MaybeUnset = Union[T, UnsetType]
         
     | 
| 
       138 
     | 
    
         
            -
            MaybeSentinel = Union[T, UndefinedType, UnsetType]
         
     | 
| 
       139 
     | 
    
         
            -
             
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
     | 
    
         
            -
            def is_sentinel(value: Any) -> bool:
         
     | 
| 
       142 
     | 
    
         
            -
                """Check if a value is any sentinel (Undefined or Unset)."""
         
     | 
| 
       143 
     | 
    
         
            -
                return value is Undefined or value is Unset
         
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
             
     | 
| 
       146 
     | 
    
         
            -
            def not_sentinel(value: Any) -> bool:
         
     | 
| 
       147 
     | 
    
         
            -
                """Check if a value is NOT a sentinel. Useful for filtering operations."""
         
     | 
| 
       148 
     | 
    
         
            -
                return value is not Undefined and value is not Unset
         
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
22 
     | 
    
         | 
| 
       151 
23 
     | 
    
         
             
            class Enum(_Enum):
         
     | 
| 
      
 24 
     | 
    
         
            +
                """Enhanced Enum with allowed() classmethod."""
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
       152 
26 
     | 
    
         
             
                @classmethod
         
     | 
| 
       153 
27 
     | 
    
         
             
                def allowed(cls) -> tuple[str, ...]:
         
     | 
| 
       154 
28 
     | 
    
         
             
                    return tuple(e.value for e in cls)
         
     | 
| 
         @@ -160,18 +34,46 @@ class KeysDict(TypedDict, total=False): 
     | 
|
| 
       160 
34 
     | 
    
         
             
                key: Any  # Represents any key-type pair
         
     | 
| 
       161 
35 
     | 
    
         | 
| 
       162 
36 
     | 
    
         | 
| 
      
 37 
     | 
    
         
            +
            @dataclass(slots=True, frozen=True)
         
     | 
| 
      
 38 
     | 
    
         
            +
            class ModelConfig:
         
     | 
| 
      
 39 
     | 
    
         
            +
                """Configuration for Params and DataClass behavior.
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                Attributes:
         
     | 
| 
      
 42 
     | 
    
         
            +
                    none_as_sentinel: If True, None is treated as a sentinel value (excluded from to_dict).
         
     | 
| 
      
 43 
     | 
    
         
            +
                    empty_as_sentinel: If True, empty collections are treated as sentinels (excluded from to_dict).
         
     | 
| 
      
 44 
     | 
    
         
            +
                    strict: If True, no sentinels allowed (all fields must have values).
         
     | 
| 
      
 45 
     | 
    
         
            +
                    prefill_unset: If True, unset fields are prefilled with Unset.
         
     | 
| 
      
 46 
     | 
    
         
            +
                    use_enum_values: If True, use enum values instead of enum instances in to_dict().
         
     | 
| 
      
 47 
     | 
    
         
            +
                """
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                # Sentinel handling (controls what gets excluded from to_dict)
         
     | 
| 
      
 50 
     | 
    
         
            +
                none_as_sentinel: bool = False
         
     | 
| 
      
 51 
     | 
    
         
            +
                empty_as_sentinel: bool = False
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                # Validation
         
     | 
| 
      
 54 
     | 
    
         
            +
                strict: bool = False
         
     | 
| 
      
 55 
     | 
    
         
            +
                prefill_unset: bool = True
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                # Serialization
         
     | 
| 
      
 58 
     | 
    
         
            +
                use_enum_values: bool = False
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
       163 
61 
     | 
    
         
             
            @dataclass(slots=True, frozen=True, init=False)
         
     | 
| 
       164 
62 
     | 
    
         
             
            class Params:
         
     | 
| 
       165 
     | 
    
         
            -
                """Base class for parameters used in various functions. 
     | 
| 
      
 63 
     | 
    
         
            +
                """Base class for parameters used in various functions.
         
     | 
| 
       166 
64 
     | 
    
         | 
| 
       167 
     | 
    
         
            -
                 
     | 
| 
       168 
     | 
    
         
            -
                """If True, None is treated as a sentinel value."""
         
     | 
| 
      
 65 
     | 
    
         
            +
                Use the ModelConfig class attribute to customize behavior:
         
     | 
| 
       169 
66 
     | 
    
         | 
| 
       170 
     | 
    
         
            -
                 
     | 
| 
       171 
     | 
    
         
            -
             
     | 
| 
      
 67 
     | 
    
         
            +
                Example:
         
     | 
| 
      
 68 
     | 
    
         
            +
                    @dataclass(slots=True, frozen=True, init=False)
         
     | 
| 
      
 69 
     | 
    
         
            +
                    class MyParams(Params):
         
     | 
| 
      
 70 
     | 
    
         
            +
                        _config: ClassVar[ModelConfig] = ModelConfig(strict=True)
         
     | 
| 
      
 71 
     | 
    
         
            +
                        param1: str
         
     | 
| 
      
 72 
     | 
    
         
            +
                        param2: int
         
     | 
| 
      
 73 
     | 
    
         
            +
                """
         
     | 
| 
       172 
74 
     | 
    
         | 
| 
       173 
     | 
    
         
            -
                 
     | 
| 
       174 
     | 
    
         
            -
                """ 
     | 
| 
      
 75 
     | 
    
         
            +
                _config: ClassVar[ModelConfig] = ModelConfig()
         
     | 
| 
      
 76 
     | 
    
         
            +
                """Configuration for this Params class."""
         
     | 
| 
       175 
77 
     | 
    
         | 
| 
       176 
78 
     | 
    
         
             
                _allowed_keys: ClassVar[set[str]] = field(
         
     | 
| 
       177 
79 
     | 
    
         
             
                    default=set(), init=False, repr=False
         
     | 
| 
         @@ -193,9 +95,23 @@ class Params: 
     | 
|
| 
       193 
95 
     | 
    
         
             
                @classmethod
         
     | 
| 
       194 
96 
     | 
    
         
             
                def _is_sentinel(cls, value: Any) -> bool:
         
     | 
| 
       195 
97 
     | 
    
         
             
                    """Check if a value is a sentinel (Undefined or Unset)."""
         
     | 
| 
       196 
     | 
    
         
            -
                     
     | 
| 
       197 
     | 
    
         
            -
                         
     | 
| 
       198 
     | 
    
         
            -
             
     | 
| 
      
 98 
     | 
    
         
            +
                    return is_sentinel(
         
     | 
| 
      
 99 
     | 
    
         
            +
                        value,
         
     | 
| 
      
 100 
     | 
    
         
            +
                        none_as_sentinel=cls._config.none_as_sentinel,
         
     | 
| 
      
 101 
     | 
    
         
            +
                        empty_as_sentinel=cls._config.empty_as_sentinel,
         
     | 
| 
      
 102 
     | 
    
         
            +
                    )
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                @classmethod
         
     | 
| 
      
 105 
     | 
    
         
            +
                def _normalize_value(cls, value: Any) -> Any:
         
     | 
| 
      
 106 
     | 
    
         
            +
                    """Normalize a value for serialization.
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                    Handles:
         
     | 
| 
      
 109 
     | 
    
         
            +
                    - Enum values if use_enum_values is True
         
     | 
| 
      
 110 
     | 
    
         
            +
                    - Can be extended for other transformations
         
     | 
| 
      
 111 
     | 
    
         
            +
                    """
         
     | 
| 
      
 112 
     | 
    
         
            +
                    if cls._config.use_enum_values and isinstance(value, _Enum):
         
     | 
| 
      
 113 
     | 
    
         
            +
                        return value.value
         
     | 
| 
      
 114 
     | 
    
         
            +
                    return value
         
     | 
| 
       199 
115 
     | 
    
         | 
| 
       200 
116 
     | 
    
         
             
                @classmethod
         
     | 
| 
       201 
117 
     | 
    
         
             
                def allowed(cls) -> set[str]:
         
     | 
| 
         @@ -210,10 +126,12 @@ class Params: 
     | 
|
| 
       210 
126 
     | 
    
         
             
                @override
         
     | 
| 
       211 
127 
     | 
    
         
             
                def _validate(self) -> None:
         
     | 
| 
       212 
128 
     | 
    
         
             
                    def _validate_strict(k):
         
     | 
| 
       213 
     | 
    
         
            -
                        if self. 
     | 
| 
      
 129 
     | 
    
         
            +
                        if self._config.strict and self._is_sentinel(
         
     | 
| 
      
 130 
     | 
    
         
            +
                            getattr(self, k, Unset)
         
     | 
| 
      
 131 
     | 
    
         
            +
                        ):
         
     | 
| 
       214 
132 
     | 
    
         
             
                            raise ValueError(f"Missing required parameter: {k}")
         
     | 
| 
       215 
133 
     | 
    
         
             
                        if (
         
     | 
| 
       216 
     | 
    
         
            -
                            self. 
     | 
| 
      
 134 
     | 
    
         
            +
                            self._config.prefill_unset
         
     | 
| 
       217 
135 
     | 
    
         
             
                            and getattr(self, k, Undefined) is Undefined
         
     | 
| 
       218 
136 
     | 
    
         
             
                        ):
         
     | 
| 
       219 
137 
     | 
    
         
             
                            object.__setattr__(self, k, Unset)
         
     | 
| 
         @@ -236,14 +154,14 @@ class Params: 
     | 
|
| 
       236 
154 
     | 
    
         
             
                    data = {}
         
     | 
| 
       237 
155 
     | 
    
         
             
                    exclude = exclude or set()
         
     | 
| 
       238 
156 
     | 
    
         
             
                    for k in self.allowed():
         
     | 
| 
       239 
     | 
    
         
            -
                        if k not in exclude 
     | 
| 
       240 
     | 
    
         
            -
                            v  
     | 
| 
       241 
     | 
    
         
            -
             
     | 
| 
       242 
     | 
    
         
            -
             
     | 
| 
      
 157 
     | 
    
         
            +
                        if k not in exclude:
         
     | 
| 
      
 158 
     | 
    
         
            +
                            v = getattr(self, k, Undefined)
         
     | 
| 
      
 159 
     | 
    
         
            +
                            if not self._is_sentinel(v):
         
     | 
| 
      
 160 
     | 
    
         
            +
                                data[k] = self._normalize_value(v)
         
     | 
| 
       243 
161 
     | 
    
         
             
                    return data
         
     | 
| 
       244 
162 
     | 
    
         | 
| 
       245 
163 
     | 
    
         
             
                def __hash__(self) -> int:
         
     | 
| 
       246 
     | 
    
         
            -
                    from  
     | 
| 
      
 164 
     | 
    
         
            +
                    from .._hash import hash_dict
         
     | 
| 
       247 
165 
     | 
    
         | 
| 
       248 
166 
     | 
    
         
             
                    return hash_dict(self.to_dict())
         
     | 
| 
       249 
167 
     | 
    
         | 
| 
         @@ -261,16 +179,20 @@ class Params: 
     | 
|
| 
       261 
179 
     | 
    
         | 
| 
       262 
180 
     | 
    
         
             
            @dataclass(slots=True)
         
     | 
| 
       263 
181 
     | 
    
         
             
            class DataClass:
         
     | 
| 
       264 
     | 
    
         
            -
                """A base class for data classes with strict parameter handling. 
     | 
| 
      
 182 
     | 
    
         
            +
                """A base class for data classes with strict parameter handling.
         
     | 
| 
       265 
183 
     | 
    
         | 
| 
       266 
     | 
    
         
            -
                 
     | 
| 
       267 
     | 
    
         
            -
                """If True, None is treated as a sentinel value."""
         
     | 
| 
      
 184 
     | 
    
         
            +
                Use the ModelConfig class attribute to customize behavior:
         
     | 
| 
       268 
185 
     | 
    
         | 
| 
       269 
     | 
    
         
            -
                 
     | 
| 
       270 
     | 
    
         
            -
             
     | 
| 
      
 186 
     | 
    
         
            +
                Example:
         
     | 
| 
      
 187 
     | 
    
         
            +
                    @dataclass(slots=True)
         
     | 
| 
      
 188 
     | 
    
         
            +
                    class MyDataClass(DataClass):
         
     | 
| 
      
 189 
     | 
    
         
            +
                        _config: ClassVar[ModelConfig] = ModelConfig(strict=True, prefill_unset=False)
         
     | 
| 
      
 190 
     | 
    
         
            +
                        field1: str
         
     | 
| 
      
 191 
     | 
    
         
            +
                        field2: int
         
     | 
| 
      
 192 
     | 
    
         
            +
                """
         
     | 
| 
       271 
193 
     | 
    
         | 
| 
       272 
     | 
    
         
            -
                 
     | 
| 
       273 
     | 
    
         
            -
                """ 
     | 
| 
      
 194 
     | 
    
         
            +
                _config: ClassVar[ModelConfig] = ModelConfig()
         
     | 
| 
      
 195 
     | 
    
         
            +
                """Configuration for this DataClass."""
         
     | 
| 
       274 
196 
     | 
    
         | 
| 
       275 
197 
     | 
    
         
             
                _allowed_keys: ClassVar[set[str]] = field(
         
     | 
| 
       276 
198 
     | 
    
         
             
                    default=set(), init=False, repr=False
         
     | 
| 
         @@ -294,10 +216,12 @@ class DataClass: 
     | 
|
| 
       294 
216 
     | 
    
         
             
                @override
         
     | 
| 
       295 
217 
     | 
    
         
             
                def _validate(self) -> None:
         
     | 
| 
       296 
218 
     | 
    
         
             
                    def _validate_strict(k):
         
     | 
| 
       297 
     | 
    
         
            -
                        if self. 
     | 
| 
      
 219 
     | 
    
         
            +
                        if self._config.strict and self._is_sentinel(
         
     | 
| 
      
 220 
     | 
    
         
            +
                            getattr(self, k, Unset)
         
     | 
| 
      
 221 
     | 
    
         
            +
                        ):
         
     | 
| 
       298 
222 
     | 
    
         
             
                            raise ValueError(f"Missing required parameter: {k}")
         
     | 
| 
       299 
223 
     | 
    
         
             
                        if (
         
     | 
| 
       300 
     | 
    
         
            -
                            self. 
     | 
| 
      
 224 
     | 
    
         
            +
                            self._config.prefill_unset
         
     | 
| 
       301 
225 
     | 
    
         
             
                            and getattr(self, k, Undefined) is Undefined
         
     | 
| 
       302 
226 
     | 
    
         
             
                        ):
         
     | 
| 
       303 
227 
     | 
    
         
             
                            self.__setattr__(k, Unset)
         
     | 
| 
         @@ -309,18 +233,34 @@ class DataClass: 
     | 
|
| 
       309 
233 
     | 
    
         
             
                    data = {}
         
     | 
| 
       310 
234 
     | 
    
         
             
                    exclude = exclude or set()
         
     | 
| 
       311 
235 
     | 
    
         
             
                    for k in type(self).allowed():
         
     | 
| 
       312 
     | 
    
         
            -
                        if k not in exclude 
     | 
| 
       313 
     | 
    
         
            -
                            v  
     | 
| 
       314 
     | 
    
         
            -
             
     | 
| 
       315 
     | 
    
         
            -
             
     | 
| 
      
 236 
     | 
    
         
            +
                        if k not in exclude:
         
     | 
| 
      
 237 
     | 
    
         
            +
                            v = getattr(self, k)
         
     | 
| 
      
 238 
     | 
    
         
            +
                            if not self._is_sentinel(v):
         
     | 
| 
      
 239 
     | 
    
         
            +
                                data[k] = self._normalize_value(v)
         
     | 
| 
       316 
240 
     | 
    
         
             
                    return data
         
     | 
| 
       317 
241 
     | 
    
         | 
| 
       318 
242 
     | 
    
         
             
                @classmethod
         
     | 
| 
       319 
243 
     | 
    
         
             
                def _is_sentinel(cls, value: Any) -> bool:
         
     | 
| 
       320 
244 
     | 
    
         
             
                    """Check if a value is a sentinel (Undefined or Unset)."""
         
     | 
| 
       321 
     | 
    
         
            -
                     
     | 
| 
       322 
     | 
    
         
            -
                         
     | 
| 
       323 
     | 
    
         
            -
             
     | 
| 
      
 245 
     | 
    
         
            +
                    return is_sentinel(
         
     | 
| 
      
 246 
     | 
    
         
            +
                        value,
         
     | 
| 
      
 247 
     | 
    
         
            +
                        none_as_sentinel=cls._config.none_as_sentinel,
         
     | 
| 
      
 248 
     | 
    
         
            +
                        empty_as_sentinel=cls._config.empty_as_sentinel,
         
     | 
| 
      
 249 
     | 
    
         
            +
                    )
         
     | 
| 
      
 250 
     | 
    
         
            +
             
     | 
| 
      
 251 
     | 
    
         
            +
                @classmethod
         
     | 
| 
      
 252 
     | 
    
         
            +
                def _normalize_value(cls, value: Any) -> Any:
         
     | 
| 
      
 253 
     | 
    
         
            +
                    """Normalize a value for serialization.
         
     | 
| 
      
 254 
     | 
    
         
            +
             
     | 
| 
      
 255 
     | 
    
         
            +
                    Handles:
         
     | 
| 
      
 256 
     | 
    
         
            +
                    - Enum values if use_enum_values is True
         
     | 
| 
      
 257 
     | 
    
         
            +
                    - Can be extended for other transformations
         
     | 
| 
      
 258 
     | 
    
         
            +
                    """
         
     | 
| 
      
 259 
     | 
    
         
            +
                    from enum import Enum as _Enum
         
     | 
| 
      
 260 
     | 
    
         
            +
             
     | 
| 
      
 261 
     | 
    
         
            +
                    if cls._config.use_enum_values and isinstance(value, _Enum):
         
     | 
| 
      
 262 
     | 
    
         
            +
                        return value.value
         
     | 
| 
      
 263 
     | 
    
         
            +
                    return value
         
     | 
| 
       324 
264 
     | 
    
         | 
| 
       325 
265 
     | 
    
         
             
                def with_updates(self, **kwargs: Any) -> DataClass:
         
     | 
| 
       326 
266 
     | 
    
         
             
                    """Return a new instance with updated fields."""
         
     | 
| 
         @@ -329,7 +269,7 @@ class DataClass: 
     | 
|
| 
       329 
269 
     | 
    
         
             
                    return type(self)(**dict_)
         
     | 
| 
       330 
270 
     | 
    
         | 
| 
       331 
271 
     | 
    
         
             
                def __hash__(self) -> int:
         
     | 
| 
       332 
     | 
    
         
            -
                    from  
     | 
| 
      
 272 
     | 
    
         
            +
                    from .._hash import hash_dict
         
     | 
| 
       333 
273 
     | 
    
         | 
| 
       334 
274 
     | 
    
         
             
                    return hash_dict(self.to_dict())
         
     | 
| 
       335 
275 
     | 
    
         | 
| 
         @@ -0,0 +1,221 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            """Operable - Container for Spec collections with model generation.
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            This module provides the Operable class for managing collections of Spec objects
         
     | 
| 
      
 4 
     | 
    
         
            +
            and generating framework-specific models via adapters.
         
     | 
| 
      
 5 
     | 
    
         
            +
            """
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            from __future__ import annotations
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            from dataclasses import dataclass
         
     | 
| 
      
 10 
     | 
    
         
            +
            from typing import TYPE_CHECKING, Literal
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            from ._sentinel import MaybeUnset, Unset
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            if TYPE_CHECKING:
         
     | 
| 
      
 15 
     | 
    
         
            +
                from .spec import Spec
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            __all__ = ("Operable",)
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            @dataclass(frozen=True, slots=True, init=False)
         
     | 
| 
      
 21 
     | 
    
         
            +
            class Operable:
         
     | 
| 
      
 22 
     | 
    
         
            +
                """Collection of Spec objects with model generation capabilities.
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                Operable manages an ordered collection of Spec objects and provides
         
     | 
| 
      
 25 
     | 
    
         
            +
                methods to generate framework-specific models via adapters.
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                Attributes:
         
     | 
| 
      
 28 
     | 
    
         
            +
                    __op_fields__: Ordered tuple of Spec objects
         
     | 
| 
      
 29 
     | 
    
         
            +
                    name: Optional name for this operable
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                Example:
         
     | 
| 
      
 32 
     | 
    
         
            +
                    >>> from lionagi.ln.types import Spec, Operable
         
     | 
| 
      
 33 
     | 
    
         
            +
                    >>> specs = (
         
     | 
| 
      
 34 
     | 
    
         
            +
                    ...     Spec(str, name="username"),
         
     | 
| 
      
 35 
     | 
    
         
            +
                    ...     Spec(int, name="age"),
         
     | 
| 
      
 36 
     | 
    
         
            +
                    ... )
         
     | 
| 
      
 37 
     | 
    
         
            +
                    >>> operable = Operable(specs, name="User")
         
     | 
| 
      
 38 
     | 
    
         
            +
                    >>> UserModel = operable.create_model(adapter="pydantic")
         
     | 
| 
      
 39 
     | 
    
         
            +
                """
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                __op_fields__: tuple[Spec, ...]
         
     | 
| 
      
 42 
     | 
    
         
            +
                name: str | None
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def __init__(
         
     | 
| 
      
 45 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 46 
     | 
    
         
            +
                    specs: tuple[Spec, ...] | list[Spec] = (),
         
     | 
| 
      
 47 
     | 
    
         
            +
                    *,
         
     | 
| 
      
 48 
     | 
    
         
            +
                    name: str | None = None,
         
     | 
| 
      
 49 
     | 
    
         
            +
                ):
         
     | 
| 
      
 50 
     | 
    
         
            +
                    """Initialize Operable with specs.
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 53 
     | 
    
         
            +
                        specs: Tuple or list of Spec objects
         
     | 
| 
      
 54 
     | 
    
         
            +
                        name: Optional name for this operable
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    Raises:
         
     | 
| 
      
 57 
     | 
    
         
            +
                        TypeError: If specs contains non-Spec objects
         
     | 
| 
      
 58 
     | 
    
         
            +
                        ValueError: If specs contains duplicate field names
         
     | 
| 
      
 59 
     | 
    
         
            +
                    """
         
     | 
| 
      
 60 
     | 
    
         
            +
                    # Import here to avoid circular import
         
     | 
| 
      
 61 
     | 
    
         
            +
                    from .spec import Spec
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    # Convert to tuple if list
         
     | 
| 
      
 64 
     | 
    
         
            +
                    if isinstance(specs, list):
         
     | 
| 
      
 65 
     | 
    
         
            +
                        specs = tuple(specs)
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    # Validate all items are Spec objects
         
     | 
| 
      
 68 
     | 
    
         
            +
                    for i, item in enumerate(specs):
         
     | 
| 
      
 69 
     | 
    
         
            +
                        if not isinstance(item, Spec):
         
     | 
| 
      
 70 
     | 
    
         
            +
                            raise TypeError(
         
     | 
| 
      
 71 
     | 
    
         
            +
                                f"All specs must be Spec objects, got {type(item).__name__} "
         
     | 
| 
      
 72 
     | 
    
         
            +
                                f"at index {i}"
         
     | 
| 
      
 73 
     | 
    
         
            +
                            )
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    # Check for duplicate names
         
     | 
| 
      
 76 
     | 
    
         
            +
                    names = [s.name for s in specs if s.name is not None]
         
     | 
| 
      
 77 
     | 
    
         
            +
                    if len(names) != len(set(names)):
         
     | 
| 
      
 78 
     | 
    
         
            +
                        from collections import Counter
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                        duplicates = [
         
     | 
| 
      
 81 
     | 
    
         
            +
                            name for name, count in Counter(names).items() if count > 1
         
     | 
| 
      
 82 
     | 
    
         
            +
                        ]
         
     | 
| 
      
 83 
     | 
    
         
            +
                        raise ValueError(
         
     | 
| 
      
 84 
     | 
    
         
            +
                            f"Duplicate field names found: {duplicates}. "
         
     | 
| 
      
 85 
     | 
    
         
            +
                            "Each spec must have a unique name."
         
     | 
| 
      
 86 
     | 
    
         
            +
                        )
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                    object.__setattr__(self, "__op_fields__", specs)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    object.__setattr__(self, "name", name)
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                def allowed(self) -> set[str]:
         
     | 
| 
      
 92 
     | 
    
         
            +
                    """Get set of allowed field names.
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                    Returns:
         
     | 
| 
      
 95 
     | 
    
         
            +
                        Set of field names from specs
         
     | 
| 
      
 96 
     | 
    
         
            +
                    """
         
     | 
| 
      
 97 
     | 
    
         
            +
                    return {i.name for i in self.__op_fields__}
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                def check_allowed(self, *args, as_boolean: bool = False):
         
     | 
| 
      
 100 
     | 
    
         
            +
                    """Check if field names are allowed.
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 103 
     | 
    
         
            +
                        *args: Field names to check
         
     | 
| 
      
 104 
     | 
    
         
            +
                        as_boolean: If True, return bool instead of raising
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                    Returns:
         
     | 
| 
      
 107 
     | 
    
         
            +
                        True if all allowed, False if as_boolean=True and not all allowed
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                    Raises:
         
     | 
| 
      
 110 
     | 
    
         
            +
                        ValueError: If as_boolean=False and not all allowed
         
     | 
| 
      
 111 
     | 
    
         
            +
                    """
         
     | 
| 
      
 112 
     | 
    
         
            +
                    if not set(args).issubset(self.allowed()):
         
     | 
| 
      
 113 
     | 
    
         
            +
                        if as_boolean:
         
     | 
| 
      
 114 
     | 
    
         
            +
                            return False
         
     | 
| 
      
 115 
     | 
    
         
            +
                        raise ValueError(
         
     | 
| 
      
 116 
     | 
    
         
            +
                            "Some specified fields are not allowed: "
         
     | 
| 
      
 117 
     | 
    
         
            +
                            f"{set(args).difference(self.allowed())}"
         
     | 
| 
      
 118 
     | 
    
         
            +
                        )
         
     | 
| 
      
 119 
     | 
    
         
            +
                    return True
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                def get(self, key: str, /, default=Unset) -> MaybeUnset[Spec]:
         
     | 
| 
      
 122 
     | 
    
         
            +
                    """Get Spec by field name.
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 125 
     | 
    
         
            +
                        key: Field name
         
     | 
| 
      
 126 
     | 
    
         
            +
                        default: Default value if not found
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                    Returns:
         
     | 
| 
      
 129 
     | 
    
         
            +
                        Spec object or default
         
     | 
| 
      
 130 
     | 
    
         
            +
                    """
         
     | 
| 
      
 131 
     | 
    
         
            +
                    if not self.check_allowed(key, as_boolean=True):
         
     | 
| 
      
 132 
     | 
    
         
            +
                        return default
         
     | 
| 
      
 133 
     | 
    
         
            +
                    for i in self.__op_fields__:
         
     | 
| 
      
 134 
     | 
    
         
            +
                        if i.name == key:
         
     | 
| 
      
 135 
     | 
    
         
            +
                            return i
         
     | 
| 
      
 136 
     | 
    
         
            +
                    return default
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                def get_specs(
         
     | 
| 
      
 139 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 140 
     | 
    
         
            +
                    *,
         
     | 
| 
      
 141 
     | 
    
         
            +
                    include: set[str] | None = None,
         
     | 
| 
      
 142 
     | 
    
         
            +
                    exclude: set[str] | None = None,
         
     | 
| 
      
 143 
     | 
    
         
            +
                ) -> tuple[Spec, ...]:
         
     | 
| 
      
 144 
     | 
    
         
            +
                    """Get filtered tuple of Specs.
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 147 
     | 
    
         
            +
                        include: Only include these field names
         
     | 
| 
      
 148 
     | 
    
         
            +
                        exclude: Exclude these field names
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                    Returns:
         
     | 
| 
      
 151 
     | 
    
         
            +
                        Filtered tuple of Specs
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                    Raises:
         
     | 
| 
      
 154 
     | 
    
         
            +
                        ValueError: If both include and exclude specified, or if invalid names
         
     | 
| 
      
 155 
     | 
    
         
            +
                    """
         
     | 
| 
      
 156 
     | 
    
         
            +
                    if include is not None and exclude is not None:
         
     | 
| 
      
 157 
     | 
    
         
            +
                        raise ValueError("Cannot specify both include and exclude")
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                    if include:
         
     | 
| 
      
 160 
     | 
    
         
            +
                        if self.check_allowed(*include, as_boolean=True) is False:
         
     | 
| 
      
 161 
     | 
    
         
            +
                            raise ValueError(
         
     | 
| 
      
 162 
     | 
    
         
            +
                                "Some specified fields are not allowed: "
         
     | 
| 
      
 163 
     | 
    
         
            +
                                f"{set(include).difference(self.allowed())}"
         
     | 
| 
      
 164 
     | 
    
         
            +
                            )
         
     | 
| 
      
 165 
     | 
    
         
            +
                        return tuple(
         
     | 
| 
      
 166 
     | 
    
         
            +
                            self.get(i) for i in include if self.get(i) is not Unset
         
     | 
| 
      
 167 
     | 
    
         
            +
                        )
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                    if exclude:
         
     | 
| 
      
 170 
     | 
    
         
            +
                        _discards = {
         
     | 
| 
      
 171 
     | 
    
         
            +
                            self.get(i) for i in exclude if self.get(i) is not Unset
         
     | 
| 
      
 172 
     | 
    
         
            +
                        }
         
     | 
| 
      
 173 
     | 
    
         
            +
                        return tuple(s for s in self.__op_fields__ if s not in _discards)
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                    return self.__op_fields__
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                def create_model(
         
     | 
| 
      
 178 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 179 
     | 
    
         
            +
                    adapter: Literal["pydantic"] = "pydantic",
         
     | 
| 
      
 180 
     | 
    
         
            +
                    model_name: str | None = None,
         
     | 
| 
      
 181 
     | 
    
         
            +
                    include: set[str] | None = None,
         
     | 
| 
      
 182 
     | 
    
         
            +
                    exclude: set[str] | None = None,
         
     | 
| 
      
 183 
     | 
    
         
            +
                    **kw,
         
     | 
| 
      
 184 
     | 
    
         
            +
                ):
         
     | 
| 
      
 185 
     | 
    
         
            +
                    """Create framework-specific model from specs.
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 188 
     | 
    
         
            +
                        adapter: Adapter type (currently only "pydantic")
         
     | 
| 
      
 189 
     | 
    
         
            +
                        model_name: Name for generated model
         
     | 
| 
      
 190 
     | 
    
         
            +
                        include: Only include these fields
         
     | 
| 
      
 191 
     | 
    
         
            +
                        exclude: Exclude these fields
         
     | 
| 
      
 192 
     | 
    
         
            +
                        **kw: Additional adapter-specific kwargs
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                    Returns:
         
     | 
| 
      
 195 
     | 
    
         
            +
                        Generated model class
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                    Raises:
         
     | 
| 
      
 198 
     | 
    
         
            +
                        ImportError: If adapter not installed
         
     | 
| 
      
 199 
     | 
    
         
            +
                        ValueError: If adapter not supported
         
     | 
| 
      
 200 
     | 
    
         
            +
                    """
         
     | 
| 
      
 201 
     | 
    
         
            +
                    match adapter:
         
     | 
| 
      
 202 
     | 
    
         
            +
                        case "pydantic":
         
     | 
| 
      
 203 
     | 
    
         
            +
                            try:
         
     | 
| 
      
 204 
     | 
    
         
            +
                                from lionagi.adapters.spec_adapters import (
         
     | 
| 
      
 205 
     | 
    
         
            +
                                    PydanticSpecAdapter,
         
     | 
| 
      
 206 
     | 
    
         
            +
                                )
         
     | 
| 
      
 207 
     | 
    
         
            +
                            except ImportError as e:
         
     | 
| 
      
 208 
     | 
    
         
            +
                                raise ImportError(
         
     | 
| 
      
 209 
     | 
    
         
            +
                                    "PydanticSpecAdapter requires Pydantic. "
         
     | 
| 
      
 210 
     | 
    
         
            +
                                    "Install with: pip install pydantic"
         
     | 
| 
      
 211 
     | 
    
         
            +
                                ) from e
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
                            kws = {
         
     | 
| 
      
 214 
     | 
    
         
            +
                                "model_name": model_name or self.name or "DynamicModel",
         
     | 
| 
      
 215 
     | 
    
         
            +
                                "include": include,
         
     | 
| 
      
 216 
     | 
    
         
            +
                                "exclude": exclude,
         
     | 
| 
      
 217 
     | 
    
         
            +
                                **kw,
         
     | 
| 
      
 218 
     | 
    
         
            +
                            }
         
     | 
| 
      
 219 
     | 
    
         
            +
                            return PydanticSpecAdapter.create_model(self, **kws)
         
     | 
| 
      
 220 
     | 
    
         
            +
                        case _:
         
     | 
| 
      
 221 
     | 
    
         
            +
                            raise ValueError(f"Unsupported adapter: {adapter}")
         
     |