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
 
| 
         @@ -0,0 +1,203 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
         
     | 
| 
      
 2 
     | 
    
         
            +
            # SPDX-License-Identifier: Apache-2.0
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            """Step factory methods for creating configured Operative instances."""
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            from typing import TYPE_CHECKING, Literal
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            from lionagi.ln.types import Operable, Spec
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            from ..fields import get_default_field
         
     | 
| 
      
 11 
     | 
    
         
            +
            from .operative import Operative
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            if TYPE_CHECKING:
         
     | 
| 
      
 14 
     | 
    
         
            +
                from pydantic import BaseModel
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            class Step:
         
     | 
| 
      
 18 
     | 
    
         
            +
                """Factory methods for common Operative patterns.
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                Provides methods to create Operative instances with pre-configured
         
     | 
| 
      
 21 
     | 
    
         
            +
                field specifications for common patterns like ReAct, QA, and task execution.
         
     | 
| 
      
 22 
     | 
    
         
            +
                """
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                @staticmethod
         
     | 
| 
      
 25 
     | 
    
         
            +
                def request_operative(
         
     | 
| 
      
 26 
     | 
    
         
            +
                    *,
         
     | 
| 
      
 27 
     | 
    
         
            +
                    name: str | None = None,
         
     | 
| 
      
 28 
     | 
    
         
            +
                    operative_name: str | None = None,  # backward compat
         
     | 
| 
      
 29 
     | 
    
         
            +
                    adapter: Literal["pydantic"] = "pydantic",
         
     | 
| 
      
 30 
     | 
    
         
            +
                    reason: bool = False,
         
     | 
| 
      
 31 
     | 
    
         
            +
                    actions: bool = False,
         
     | 
| 
      
 32 
     | 
    
         
            +
                    fields: dict[str, Spec] | None = None,
         
     | 
| 
      
 33 
     | 
    
         
            +
                    field_models: list | None = None,  # backward compat
         
     | 
| 
      
 34 
     | 
    
         
            +
                    max_retries: int = 3,
         
     | 
| 
      
 35 
     | 
    
         
            +
                    auto_retry_parse: bool = True,
         
     | 
| 
      
 36 
     | 
    
         
            +
                    base_type: type["BaseModel"] | None = None,
         
     | 
| 
      
 37 
     | 
    
         
            +
                    # Deprecated/ignored parameters for backward compatibility
         
     | 
| 
      
 38 
     | 
    
         
            +
                    parse_kwargs: dict | None = None,
         
     | 
| 
      
 39 
     | 
    
         
            +
                    exclude_fields: list | None = None,
         
     | 
| 
      
 40 
     | 
    
         
            +
                    field_descriptions: dict | None = None,
         
     | 
| 
      
 41 
     | 
    
         
            +
                    inherit_base: bool = True,
         
     | 
| 
      
 42 
     | 
    
         
            +
                    config_dict: dict | None = None,
         
     | 
| 
      
 43 
     | 
    
         
            +
                    doc: str | None = None,
         
     | 
| 
      
 44 
     | 
    
         
            +
                    frozen: bool = False,
         
     | 
| 
      
 45 
     | 
    
         
            +
                    new_model_name: str | None = None,
         
     | 
| 
      
 46 
     | 
    
         
            +
                    parameter_fields: dict | None = None,
         
     | 
| 
      
 47 
     | 
    
         
            +
                    request_params: dict | None = None,
         
     | 
| 
      
 48 
     | 
    
         
            +
                    **kwargs,
         
     | 
| 
      
 49 
     | 
    
         
            +
                ) -> Operative:
         
     | 
| 
      
 50 
     | 
    
         
            +
                    """Create request-configured Operative with common field patterns.
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 53 
     | 
    
         
            +
                        name: Operative name
         
     | 
| 
      
 54 
     | 
    
         
            +
                        operative_name: (Deprecated) Use 'name' instead
         
     | 
| 
      
 55 
     | 
    
         
            +
                        adapter: Validation framework
         
     | 
| 
      
 56 
     | 
    
         
            +
                        reason: Add reasoning trace field
         
     | 
| 
      
 57 
     | 
    
         
            +
                        actions: Add action request/response fields
         
     | 
| 
      
 58 
     | 
    
         
            +
                        fields: Additional custom field specs (dict[str, Spec])
         
     | 
| 
      
 59 
     | 
    
         
            +
                        field_models: (Deprecated) Use 'fields' instead - list of FieldModel/Spec
         
     | 
| 
      
 60 
     | 
    
         
            +
                        max_retries: Max validation retries
         
     | 
| 
      
 61 
     | 
    
         
            +
                        auto_retry_parse: Auto-retry on parse failure
         
     | 
| 
      
 62 
     | 
    
         
            +
                        base_type: Base Pydantic model to extend
         
     | 
| 
      
 63 
     | 
    
         
            +
                        parse_kwargs: (Deprecated) Ignored - parse config handled internally
         
     | 
| 
      
 64 
     | 
    
         
            +
                        exclude_fields: (Deprecated) Ignored
         
     | 
| 
      
 65 
     | 
    
         
            +
                        field_descriptions: (Deprecated) Ignored
         
     | 
| 
      
 66 
     | 
    
         
            +
                        inherit_base: (Deprecated) Ignored
         
     | 
| 
      
 67 
     | 
    
         
            +
                        config_dict: (Deprecated) Ignored
         
     | 
| 
      
 68 
     | 
    
         
            +
                        doc: (Deprecated) Ignored
         
     | 
| 
      
 69 
     | 
    
         
            +
                        frozen: (Deprecated) Ignored
         
     | 
| 
      
 70 
     | 
    
         
            +
                        new_model_name: (Deprecated) Ignored
         
     | 
| 
      
 71 
     | 
    
         
            +
                        parameter_fields: (Deprecated) Ignored
         
     | 
| 
      
 72 
     | 
    
         
            +
                        request_params: (Deprecated) Ignored
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                    Returns:
         
     | 
| 
      
 75 
     | 
    
         
            +
                        Configured Operative instance
         
     | 
| 
      
 76 
     | 
    
         
            +
                    """
         
     | 
| 
      
 77 
     | 
    
         
            +
                    # Handle backward compatibility
         
     | 
| 
      
 78 
     | 
    
         
            +
                    name = name or operative_name
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                    # Convert field_models list to fields dict if provided
         
     | 
| 
      
 81 
     | 
    
         
            +
                    if field_models and not fields:
         
     | 
| 
      
 82 
     | 
    
         
            +
                        from lionagi.models import FieldModel
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                        fields = {}
         
     | 
| 
      
 85 
     | 
    
         
            +
                        for fm in field_models:
         
     | 
| 
      
 86 
     | 
    
         
            +
                            # Convert FieldModel to Spec if needed
         
     | 
| 
      
 87 
     | 
    
         
            +
                            if isinstance(fm, FieldModel):
         
     | 
| 
      
 88 
     | 
    
         
            +
                                spec = fm.to_spec()
         
     | 
| 
      
 89 
     | 
    
         
            +
                            elif isinstance(fm, Spec):
         
     | 
| 
      
 90 
     | 
    
         
            +
                                spec = fm
         
     | 
| 
      
 91 
     | 
    
         
            +
                            else:
         
     | 
| 
      
 92 
     | 
    
         
            +
                                continue  # Skip invalid types
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                            # Use spec name as key
         
     | 
| 
      
 95 
     | 
    
         
            +
                            if spec.name:
         
     | 
| 
      
 96 
     | 
    
         
            +
                                fields[spec.name] = spec
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                    # Build fields dict to avoid duplicates (dict preserves insertion order in Python 3.7+)
         
     | 
| 
      
 99 
     | 
    
         
            +
                    fields_dict = {}
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                    # Add common fields (convert FieldModel to Spec)
         
     | 
| 
      
 102 
     | 
    
         
            +
                    if reason:
         
     | 
| 
      
 103 
     | 
    
         
            +
                        reason_spec = get_default_field("reason").to_spec()
         
     | 
| 
      
 104 
     | 
    
         
            +
                        fields_dict["reason"] = reason_spec
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                    if actions:
         
     | 
| 
      
 107 
     | 
    
         
            +
                        fields_dict["action_required"] = get_default_field(
         
     | 
| 
      
 108 
     | 
    
         
            +
                            "action_required"
         
     | 
| 
      
 109 
     | 
    
         
            +
                        ).to_spec()
         
     | 
| 
      
 110 
     | 
    
         
            +
                        fields_dict["action_requests"] = get_default_field(
         
     | 
| 
      
 111 
     | 
    
         
            +
                            "action_requests"
         
     | 
| 
      
 112 
     | 
    
         
            +
                        ).to_spec()
         
     | 
| 
      
 113 
     | 
    
         
            +
                        fields_dict["action_responses"] = get_default_field(
         
     | 
| 
      
 114 
     | 
    
         
            +
                            "action_responses"
         
     | 
| 
      
 115 
     | 
    
         
            +
                        ).to_spec()
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                    # Add custom fields (will override defaults if same name)
         
     | 
| 
      
 118 
     | 
    
         
            +
                    if fields:
         
     | 
| 
      
 119 
     | 
    
         
            +
                        for field_name, spec in fields.items():
         
     | 
| 
      
 120 
     | 
    
         
            +
                            # Ensure spec has name
         
     | 
| 
      
 121 
     | 
    
         
            +
                            if not spec.name:
         
     | 
| 
      
 122 
     | 
    
         
            +
                                # Update spec with name using Spec metadata update
         
     | 
| 
      
 123 
     | 
    
         
            +
                                spec = Spec(
         
     | 
| 
      
 124 
     | 
    
         
            +
                                    spec.base_type,
         
     | 
| 
      
 125 
     | 
    
         
            +
                                    name=field_name,
         
     | 
| 
      
 126 
     | 
    
         
            +
                                    metadata=spec.metadata,
         
     | 
| 
      
 127 
     | 
    
         
            +
                                )
         
     | 
| 
      
 128 
     | 
    
         
            +
                            fields_dict[spec.name] = spec
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                    # Convert to list
         
     | 
| 
      
 131 
     | 
    
         
            +
                    all_fields = list(fields_dict.values())
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                    # Create Operable with all fields
         
     | 
| 
      
 134 
     | 
    
         
            +
                    operable = Operable(
         
     | 
| 
      
 135 
     | 
    
         
            +
                        tuple(all_fields),
         
     | 
| 
      
 136 
     | 
    
         
            +
                        name=name or (base_type.__name__ if base_type else "Operative"),
         
     | 
| 
      
 137 
     | 
    
         
            +
                    )
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                    # Request excludes action_responses
         
     | 
| 
      
 140 
     | 
    
         
            +
                    request_exclude = {"action_responses"} if actions else set()
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                    return Operative(
         
     | 
| 
      
 143 
     | 
    
         
            +
                        name=name,
         
     | 
| 
      
 144 
     | 
    
         
            +
                        adapter=adapter,
         
     | 
| 
      
 145 
     | 
    
         
            +
                        max_retries=max_retries,
         
     | 
| 
      
 146 
     | 
    
         
            +
                        auto_retry_parse=auto_retry_parse,
         
     | 
| 
      
 147 
     | 
    
         
            +
                        base_type=base_type,
         
     | 
| 
      
 148 
     | 
    
         
            +
                        operable=operable,
         
     | 
| 
      
 149 
     | 
    
         
            +
                        request_exclude=request_exclude,
         
     | 
| 
      
 150 
     | 
    
         
            +
                    )
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                @staticmethod
         
     | 
| 
      
 153 
     | 
    
         
            +
                def respond_operative(
         
     | 
| 
      
 154 
     | 
    
         
            +
                    operative: Operative,
         
     | 
| 
      
 155 
     | 
    
         
            +
                    additional_fields: dict[str, Spec] | None = None,
         
     | 
| 
      
 156 
     | 
    
         
            +
                ) -> Operative:
         
     | 
| 
      
 157 
     | 
    
         
            +
                    """Create response type from operative.
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                    Args:
         
     | 
| 
      
 160 
     | 
    
         
            +
                        operative: Source operative with all fields
         
     | 
| 
      
 161 
     | 
    
         
            +
                        additional_fields: Extra fields for response
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                    Returns:
         
     | 
| 
      
 164 
     | 
    
         
            +
                        Operative with response type configured
         
     | 
| 
      
 165 
     | 
    
         
            +
                    """
         
     | 
| 
      
 166 
     | 
    
         
            +
                    # If additional fields provided, create new Operative
         
     | 
| 
      
 167 
     | 
    
         
            +
                    if additional_fields:
         
     | 
| 
      
 168 
     | 
    
         
            +
                        # Get existing fields
         
     | 
| 
      
 169 
     | 
    
         
            +
                        existing_fields = list(operative.operable.__op_fields__)
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                        # Add new fields
         
     | 
| 
      
 172 
     | 
    
         
            +
                        for field_name, spec in additional_fields.items():
         
     | 
| 
      
 173 
     | 
    
         
            +
                            if not spec.name:
         
     | 
| 
      
 174 
     | 
    
         
            +
                                spec = Spec(
         
     | 
| 
      
 175 
     | 
    
         
            +
                                    spec.base_type,
         
     | 
| 
      
 176 
     | 
    
         
            +
                                    name=field_name,
         
     | 
| 
      
 177 
     | 
    
         
            +
                                    metadata=spec.metadata,
         
     | 
| 
      
 178 
     | 
    
         
            +
                                )
         
     | 
| 
      
 179 
     | 
    
         
            +
                            existing_fields.append(spec)
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                        # Create new Operable
         
     | 
| 
      
 182 
     | 
    
         
            +
                        new_operable = Operable(
         
     | 
| 
      
 183 
     | 
    
         
            +
                            tuple(existing_fields),
         
     | 
| 
      
 184 
     | 
    
         
            +
                            name=operative.name,
         
     | 
| 
      
 185 
     | 
    
         
            +
                        )
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
                        # Create new Operative
         
     | 
| 
      
 188 
     | 
    
         
            +
                        return Operative(
         
     | 
| 
      
 189 
     | 
    
         
            +
                            name=operative.name,
         
     | 
| 
      
 190 
     | 
    
         
            +
                            adapter=operative.adapter,
         
     | 
| 
      
 191 
     | 
    
         
            +
                            max_retries=operative.max_retries,
         
     | 
| 
      
 192 
     | 
    
         
            +
                            auto_retry_parse=operative.auto_retry_parse,
         
     | 
| 
      
 193 
     | 
    
         
            +
                            base_type=operative.base_type,
         
     | 
| 
      
 194 
     | 
    
         
            +
                            operable=new_operable,
         
     | 
| 
      
 195 
     | 
    
         
            +
                            request_exclude=operative.request_exclude,
         
     | 
| 
      
 196 
     | 
    
         
            +
                        )
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
                    # Otherwise just create response model
         
     | 
| 
      
 199 
     | 
    
         
            +
                    operative.create_response_model()
         
     | 
| 
      
 200 
     | 
    
         
            +
                    return operative
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
             
     | 
| 
      
 203 
     | 
    
         
            +
            __all__ = ("Step",)
         
     | 
| 
         @@ -68,11 +68,17 @@ def get_choice_representation(choice: Any) -> str: 
     | 
|
| 
       68 
68 
     | 
    
         
             
                    return choice
         
     | 
| 
       69 
69 
     | 
    
         | 
| 
       70 
70 
     | 
    
         
             
                if isinstance(choice, BaseModel):
         
     | 
| 
       71 
     | 
    
         
            -
                     
     | 
| 
      
 71 
     | 
    
         
            +
                    import json
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                    schema = choice.model_json_schema()
         
     | 
| 
      
 74 
     | 
    
         
            +
                    return f"{choice.__class__.__name__}:\n{json.dumps(schema, indent=2)}"
         
     | 
| 
       72 
75 
     | 
    
         | 
| 
       73 
76 
     | 
    
         
             
                if isinstance(choice, Enum):
         
     | 
| 
       74 
77 
     | 
    
         
             
                    return get_choice_representation(choice.value)
         
     | 
| 
       75 
78 
     | 
    
         | 
| 
      
 79 
     | 
    
         
            +
                # Handle other types (int, dict, etc.) by converting to string
         
     | 
| 
      
 80 
     | 
    
         
            +
                return str(choice)
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
       76 
82 
     | 
    
         | 
| 
       77 
83 
     | 
    
         
             
            def parse_selection(selection_str: str, choices: Any):
         
     | 
| 
       78 
84 
     | 
    
         
             
                select_from = []
         
     | 
    
        lionagi/operations/types.py
    CHANGED
    
    | 
         @@ -7,9 +7,9 @@ from typing import ClassVar, Literal 
     | 
|
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            from pydantic import BaseModel, JsonValue
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
            from lionagi.ln 
     | 
| 
      
 10 
     | 
    
         
            +
            from lionagi.ln import AlcallParams
         
     | 
| 
       11 
11 
     | 
    
         
             
            from lionagi.ln.fuzzy import FuzzyMatchKeysParams
         
     | 
| 
       12 
     | 
    
         
            -
            from lionagi.ln.types import Params
         
     | 
| 
      
 12 
     | 
    
         
            +
            from lionagi.ln.types import ModelConfig, Params
         
     | 
| 
       13 
13 
     | 
    
         
             
            from lionagi.protocols.action.tool import ToolRef
         
     | 
| 
       14 
14 
     | 
    
         
             
            from lionagi.protocols.types import ID, SenderRecipient
         
     | 
| 
       15 
15 
     | 
    
         
             
            from lionagi.service.imodel import iModel
         
     | 
| 
         @@ -43,7 +43,7 @@ class MorphParam(Params): 
     | 
|
| 
       43 
43 
     | 
    
         
             
                transformations between message states with well-defined parameters.
         
     | 
| 
       44 
44 
     | 
    
         
             
                """
         
     | 
| 
       45 
45 
     | 
    
         | 
| 
       46 
     | 
    
         
            -
                 
     | 
| 
      
 46 
     | 
    
         
            +
                _config: ClassVar[ModelConfig] = ModelConfig(none_as_sentinel=True)
         
     | 
| 
       47 
47 
     | 
    
         | 
| 
       48 
48 
     | 
    
         | 
| 
       49 
49 
     | 
    
         
             
            @dataclass(slots=True, frozen=True, init=False)
         
     | 
| 
         @@ -57,7 +57,7 @@ class ChatParam(MorphParam): 
     | 
|
| 
       57 
57 
     | 
    
         
             
                This gets mapped to InstructionContent.prompt_context during message creation.
         
     | 
| 
       58 
58 
     | 
    
         
             
                """
         
     | 
| 
       59 
59 
     | 
    
         | 
| 
       60 
     | 
    
         
            -
                 
     | 
| 
      
 60 
     | 
    
         
            +
                _config: ClassVar[ModelConfig] = ModelConfig(none_as_sentinel=True)
         
     | 
| 
       61 
61 
     | 
    
         
             
                guidance: JsonValue = None
         
     | 
| 
       62 
62 
     | 
    
         
             
                context: JsonValue = None
         
     | 
| 
       63 
63 
     | 
    
         
             
                sender: SenderRecipient = None
         
     | 
| 
         @@ -81,7 +81,7 @@ class InterpretParam(MorphParam): 
     | 
|
| 
       81 
81 
     | 
    
         
             
                transforming content according to specified guidelines.
         
     | 
| 
       82 
82 
     | 
    
         
             
                """
         
     | 
| 
       83 
83 
     | 
    
         | 
| 
       84 
     | 
    
         
            -
                 
     | 
| 
      
 84 
     | 
    
         
            +
                _config: ClassVar[ModelConfig] = ModelConfig(none_as_sentinel=True)
         
     | 
| 
       85 
85 
     | 
    
         
             
                domain: str = None
         
     | 
| 
       86 
86 
     | 
    
         
             
                style: str = None
         
     | 
| 
       87 
87 
     | 
    
         
             
                sample_writing: str = None
         
     | 
| 
         @@ -97,7 +97,7 @@ class ParseParam(MorphParam): 
     | 
|
| 
       97 
97 
     | 
    
         
             
                fuzzy matching, and error handling strategies.
         
     | 
| 
       98 
98 
     | 
    
         
             
                """
         
     | 
| 
       99 
99 
     | 
    
         | 
| 
       100 
     | 
    
         
            -
                 
     | 
| 
      
 100 
     | 
    
         
            +
                _config: ClassVar[ModelConfig] = ModelConfig(none_as_sentinel=True)
         
     | 
| 
       101 
101 
     | 
    
         
             
                response_format: type[BaseModel] | dict = None
         
     | 
| 
       102 
102 
     | 
    
         
             
                fuzzy_match_params: FuzzyMatchKeysParams | dict = None
         
     | 
| 
       103 
103 
     | 
    
         
             
                handle_validation: HandleValidation = "raise"
         
     | 
| 
         @@ -114,7 +114,7 @@ class ActionParam(MorphParam): 
     | 
|
| 
       114 
114 
     | 
    
         
             
                for action-based operations.
         
     | 
| 
       115 
115 
     | 
    
         
             
                """
         
     | 
| 
       116 
116 
     | 
    
         | 
| 
       117 
     | 
    
         
            -
                 
     | 
| 
      
 117 
     | 
    
         
            +
                _config: ClassVar[ModelConfig] = ModelConfig(none_as_sentinel=True)
         
     | 
| 
       118 
118 
     | 
    
         
             
                action_call_params: AlcallParams = None
         
     | 
| 
       119 
119 
     | 
    
         
             
                tools: ToolRef = None
         
     | 
| 
       120 
120 
     | 
    
         
             
                strategy: Literal["concurrent", "sequential"] = "concurrent"
         
     | 
| 
         @@ -4,7 +4,8 @@ 
     | 
|
| 
       4 
4 
     | 
    
         
             
            import logging
         
     | 
| 
       5 
5 
     | 
    
         
             
            from typing import Any
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
            from  
     | 
| 
      
 7 
     | 
    
         
            +
            from pydantic import BaseModel
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
       8 
9 
     | 
    
         
             
            from lionagi.protocols._concepts import Manager
         
     | 
| 
       9 
10 
     | 
    
         
             
            from lionagi.protocols.messages.action_request import ActionRequest
         
     | 
| 
       10 
11 
     | 
    
         
             
            from lionagi.utils import to_list
         
     | 
| 
         @@ -121,7 +122,7 @@ class ActionManager(Manager): 
     | 
|
| 
       121 
122 
     | 
    
         
             
                        self.register_tool(t, update=update)
         
     | 
| 
       122 
123 
     | 
    
         | 
| 
       123 
124 
     | 
    
         
             
                def match_tool(
         
     | 
| 
       124 
     | 
    
         
            -
                    self, action_request: ActionRequest |  
     | 
| 
      
 125 
     | 
    
         
            +
                    self, action_request: ActionRequest | BaseModel | dict
         
     | 
| 
       125 
126 
     | 
    
         
             
                ) -> FunctionCalling:
         
     | 
| 
       126 
127 
     | 
    
         
             
                    """
         
     | 
| 
       127 
128 
     | 
    
         
             
                    Convert an ActionRequest (or dict with "function"/"arguments")
         
     | 
| 
         @@ -134,9 +135,7 @@ class ActionManager(Manager): 
     | 
|
| 
       134 
135 
     | 
    
         
             
                    Returns:
         
     | 
| 
       135 
136 
     | 
    
         
             
                        FunctionCalling: The event object that can be invoked.
         
     | 
| 
       136 
137 
     | 
    
         
             
                    """
         
     | 
| 
       137 
     | 
    
         
            -
                    if not isinstance(
         
     | 
| 
       138 
     | 
    
         
            -
                        action_request, ActionRequest | ActionRequestModel | dict
         
     | 
| 
       139 
     | 
    
         
            -
                    ):
         
     | 
| 
      
 138 
     | 
    
         
            +
                    if not isinstance(action_request, ActionRequest | BaseModel | dict):
         
     | 
| 
       140 
139 
     | 
    
         
             
                        raise TypeError(f"Unsupported type {type(action_request)}")
         
     | 
| 
       141 
140 
     | 
    
         | 
| 
       142 
141 
     | 
    
         
             
                    func, args = None, None
         
     | 
| 
         @@ -155,7 +154,7 @@ class ActionManager(Manager): 
     | 
|
| 
       155 
154 
     | 
    
         | 
| 
       156 
155 
     | 
    
         
             
                async def invoke(
         
     | 
| 
       157 
156 
     | 
    
         
             
                    self,
         
     | 
| 
       158 
     | 
    
         
            -
                    func_call:  
     | 
| 
      
 157 
     | 
    
         
            +
                    func_call: BaseModel | ActionRequest,
         
     | 
| 
       159 
158 
     | 
    
         
             
                ) -> FunctionCalling:
         
     | 
| 
       160 
159 
     | 
    
         
             
                    """
         
     | 
| 
       161 
160 
     | 
    
         
             
                    High-level API to parse and run a function call.
         
     | 
    
        lionagi/protocols/contracts.py
    CHANGED
    
    | 
         @@ -24,7 +24,7 @@ class ObservableProto(Protocol): 
     | 
|
| 
       24 
24 
     | 
    
         | 
| 
       25 
25 
     | 
    
         
             
                This protocol defines the minimal contract for observable objects:
         
     | 
| 
       26 
26 
     | 
    
         
             
                they must have an 'id' property. The return type is permissive (Any)
         
     | 
| 
       27 
     | 
    
         
            -
                to maintain compatibility with V0's  
     | 
| 
      
 27 
     | 
    
         
            +
                to maintain compatibility with V0's UUID wrapper while enabling
         
     | 
| 
       28 
28 
     | 
    
         
             
                V1 evolution.
         
     | 
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
                All V0 Element subclasses automatically satisfy this protocol without
         
     | 
| 
         @@ -33,7 +33,7 @@ class ObservableProto(Protocol): 
     | 
|
| 
       33 
33 
     | 
    
         | 
| 
       34 
34 
     | 
    
         
             
                @property
         
     | 
| 
       35 
35 
     | 
    
         
             
                def id(self) -> object:
         
     | 
| 
       36 
     | 
    
         
            -
                    """Unique identifier. Accepts  
     | 
| 
      
 36 
     | 
    
         
            +
                    """Unique identifier. Accepts UUID, UUID, or string."""
         
     | 
| 
       37 
37 
     | 
    
         
             
                    ...
         
     | 
| 
       38 
38 
     | 
    
         | 
| 
       39 
39 
     | 
    
         | 
| 
         @@ -1,2 +1,24 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
         
     | 
| 
       2 
2 
     | 
    
         
             
            # SPDX-License-Identifier: Apache-2.0
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            from .element import ID, Element
         
     | 
| 
      
 5 
     | 
    
         
            +
            from .event import Event, EventStatus, Execution
         
     | 
| 
      
 6 
     | 
    
         
            +
            from .log import DataLogger, DataLoggerConfig, Log
         
     | 
| 
      
 7 
     | 
    
         
            +
            from .pile import Pile
         
     | 
| 
      
 8 
     | 
    
         
            +
            from .processor import Executor, Processor
         
     | 
| 
      
 9 
     | 
    
         
            +
            from .progression import Progression
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            __all__ = (
         
     | 
| 
      
 12 
     | 
    
         
            +
                "Element",
         
     | 
| 
      
 13 
     | 
    
         
            +
                "ID",
         
     | 
| 
      
 14 
     | 
    
         
            +
                "Event",
         
     | 
| 
      
 15 
     | 
    
         
            +
                "Execution",
         
     | 
| 
      
 16 
     | 
    
         
            +
                "Log",
         
     | 
| 
      
 17 
     | 
    
         
            +
                "DataLogger",
         
     | 
| 
      
 18 
     | 
    
         
            +
                "DataLoggerConfig",
         
     | 
| 
      
 19 
     | 
    
         
            +
                "Pile",
         
     | 
| 
      
 20 
     | 
    
         
            +
                "Progression",
         
     | 
| 
      
 21 
     | 
    
         
            +
                "Processor",
         
     | 
| 
      
 22 
     | 
    
         
            +
                "Executor",
         
     | 
| 
      
 23 
     | 
    
         
            +
                "EventStatus",
         
     | 
| 
      
 24 
     | 
    
         
            +
            )
         
     | 
| 
         @@ -19,98 +19,16 @@ from pydantic import ( 
     | 
|
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
            from lionagi import ln
         
     | 
| 
       21 
21 
     | 
    
         
             
            from lionagi._class_registry import get_class
         
     | 
| 
       22 
     | 
    
         
            -
            from lionagi._errors import IDError
         
     | 
| 
       23 
22 
     | 
    
         
             
            from lionagi.utils import import_module, to_dict
         
     | 
| 
       24 
23 
     | 
    
         | 
| 
       25 
24 
     | 
    
         
             
            from .._concepts import Collective, Observable, Ordering
         
     | 
| 
       26 
25 
     | 
    
         | 
| 
       27 
26 
     | 
    
         
             
            __all__ = (
         
     | 
| 
       28 
     | 
    
         
            -
                "IDType",
         
     | 
| 
       29 
27 
     | 
    
         
             
                "Element",
         
     | 
| 
       30 
     | 
    
         
            -
                "ID",
         
     | 
| 
       31 
28 
     | 
    
         
             
                "validate_order",
         
     | 
| 
       32 
     | 
    
         
            -
                "DEFAULT_ELEMENT_SERIALIZER",
         
     | 
| 
       33 
29 
     | 
    
         
             
            )
         
     | 
| 
       34 
30 
     | 
    
         | 
| 
       35 
31 
     | 
    
         | 
| 
       36 
     | 
    
         
            -
            class IDType:
         
     | 
| 
       37 
     | 
    
         
            -
                """Represents a UUIDv4-based identifier.
         
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
                This class wraps a UUID object and provides helper methods for
         
     | 
| 
       40 
     | 
    
         
            -
                validating and creating UUID version 4. It also implements equality
         
     | 
| 
       41 
     | 
    
         
            -
                and hashing so that it can be used as dictionary keys or in sets.
         
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
                Attributes:
         
     | 
| 
       44 
     | 
    
         
            -
                    _id (UUID): The wrapped UUID object.
         
     | 
| 
       45 
     | 
    
         
            -
                """
         
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
                __slots__ = ("_id",)
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
                def __init__(self, id: UUID) -> None:
         
     | 
| 
       50 
     | 
    
         
            -
                    """Initializes an IDType instance."""
         
     | 
| 
       51 
     | 
    
         
            -
                    self._id = id
         
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
                @classmethod
         
     | 
| 
       54 
     | 
    
         
            -
                def validate(cls, value: str | UUID | IDType) -> IDType:
         
     | 
| 
       55 
     | 
    
         
            -
                    """Validates and converts a value into an IDType.
         
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
       57 
     | 
    
         
            -
                    Returns:
         
     | 
| 
       58 
     | 
    
         
            -
                        IDType: The validated IDType object.
         
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
                    Raises:
         
     | 
| 
       61 
     | 
    
         
            -
                        IDError: If the provided value is not a valid UUIDv4.
         
     | 
| 
       62 
     | 
    
         
            -
                    """
         
     | 
| 
       63 
     | 
    
         
            -
                    if isinstance(value, IDType):
         
     | 
| 
       64 
     | 
    
         
            -
                        return value
         
     | 
| 
       65 
     | 
    
         
            -
                    try:
         
     | 
| 
       66 
     | 
    
         
            -
                        return cls(UUID(str(value), version=4))
         
     | 
| 
       67 
     | 
    
         
            -
                    except ValueError:
         
     | 
| 
       68 
     | 
    
         
            -
                        raise IDError(f"Invalid ID: {value}") from None
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
                @classmethod
         
     | 
| 
       71 
     | 
    
         
            -
                def create(cls) -> IDType:
         
     | 
| 
       72 
     | 
    
         
            -
                    """Creates a new IDType with a randomly generated UUIDv4.
         
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
                    Returns:
         
     | 
| 
       75 
     | 
    
         
            -
                        IDType: A new IDType instance with a random UUIDv4.
         
     | 
| 
       76 
     | 
    
         
            -
                    """
         
     | 
| 
       77 
     | 
    
         
            -
                    return cls(uuid4())
         
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
                def __str__(self) -> str:
         
     | 
| 
       80 
     | 
    
         
            -
                    """Returns the string representation of the underlying UUID.
         
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
                    Returns:
         
     | 
| 
       83 
     | 
    
         
            -
                        str: The string form of this IDType's UUID.
         
     | 
| 
       84 
     | 
    
         
            -
                    """
         
     | 
| 
       85 
     | 
    
         
            -
                    return str(self._id)
         
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
                def __repr__(self) -> str:
         
     | 
| 
       88 
     | 
    
         
            -
                    """Returns the unambiguous string representation of this IDType.
         
     | 
| 
       89 
     | 
    
         
            -
             
     | 
| 
       90 
     | 
    
         
            -
                    Returns:
         
     | 
| 
       91 
     | 
    
         
            -
                        str: A developer-friendly string for debugging.
         
     | 
| 
       92 
     | 
    
         
            -
                    """
         
     | 
| 
       93 
     | 
    
         
            -
                    return f"IDType({self._id})"
         
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
                def __eq__(self, other: Any) -> bool:
         
     | 
| 
       96 
     | 
    
         
            -
                    """Checks equality with another IDType based on UUID value.
         
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
       98 
     | 
    
         
            -
                    Returns:
         
     | 
| 
       99 
     | 
    
         
            -
                        bool: True if both have the same underlying UUID; False otherwise.
         
     | 
| 
       100 
     | 
    
         
            -
                    """
         
     | 
| 
       101 
     | 
    
         
            -
                    if not isinstance(other, IDType):
         
     | 
| 
       102 
     | 
    
         
            -
                        return NotImplemented
         
     | 
| 
       103 
     | 
    
         
            -
                    return self._id == other._id
         
     | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
       105 
     | 
    
         
            -
                def __hash__(self) -> int:
         
     | 
| 
       106 
     | 
    
         
            -
                    """Returns a hash based on the underlying UUID.
         
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
       108 
     | 
    
         
            -
                    Returns:
         
     | 
| 
       109 
     | 
    
         
            -
                        int: The hash of this object, allowing IDType to be dictionary keys.
         
     | 
| 
       110 
     | 
    
         
            -
                    """
         
     | 
| 
       111 
     | 
    
         
            -
                    return hash(self._id)
         
     | 
| 
       112 
     | 
    
         
            -
             
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
32 
     | 
    
         
             
            class Element(BaseModel, Observable):
         
     | 
| 
       115 
33 
     | 
    
         
             
                """Basic identifiable, timestamped element.
         
     | 
| 
       116 
34 
     | 
    
         | 
| 
         @@ -119,7 +37,7 @@ class Element(BaseModel, Observable): 
     | 
|
| 
       119 
37 
     | 
    
         
             
                dictionary.
         
     | 
| 
       120 
38 
     | 
    
         | 
| 
       121 
39 
     | 
    
         
             
                Attributes:
         
     | 
| 
       122 
     | 
    
         
            -
                    id ( 
     | 
| 
      
 40 
     | 
    
         
            +
                    id (UUID):
         
     | 
| 
       123 
41 
     | 
    
         
             
                        A unique ID based on UUIDv4 (defaults to a newly generated one).
         
     | 
| 
       124 
42 
     | 
    
         
             
                    created_at (float):
         
     | 
| 
       125 
43 
     | 
    
         
             
                        The creation timestamp as a float (Unix epoch). Defaults to
         
     | 
| 
         @@ -135,8 +53,8 @@ class Element(BaseModel, Observable): 
     | 
|
| 
       135 
53 
     | 
    
         
             
                    extra="forbid",
         
     | 
| 
       136 
54 
     | 
    
         
             
                )
         
     | 
| 
       137 
55 
     | 
    
         | 
| 
       138 
     | 
    
         
            -
                id:  
     | 
| 
       139 
     | 
    
         
            -
                    default_factory= 
     | 
| 
      
 56 
     | 
    
         
            +
                id: UUID = Field(
         
     | 
| 
      
 57 
     | 
    
         
            +
                    default_factory=uuid4,
         
     | 
| 
       140 
58 
     | 
    
         
             
                    title="ID",
         
     | 
| 
       141 
59 
     | 
    
         
             
                    description="Unique identifier for this element.",
         
     | 
| 
       142 
60 
     | 
    
         
             
                    frozen=True,
         
     | 
| 
         @@ -211,12 +129,14 @@ class Element(BaseModel, Observable): 
     | 
|
| 
       211 
129 
     | 
    
         
             
                        raise ValueError(f"Invalid created_at: {val}") from None
         
     | 
| 
       212 
130 
     | 
    
         | 
| 
       213 
131 
     | 
    
         
             
                @field_validator("id", mode="before")
         
     | 
| 
       214 
     | 
    
         
            -
                def  
     | 
| 
       215 
     | 
    
         
            -
                    """Ensures `id` is validated as an  
     | 
| 
       216 
     | 
    
         
            -
                     
     | 
| 
      
 132 
     | 
    
         
            +
                def _ensure_UUID(cls, val: UUID | str) -> UUID:
         
     | 
| 
      
 133 
     | 
    
         
            +
                    """Ensures `id` is validated as an UUID."""
         
     | 
| 
      
 134 
     | 
    
         
            +
                    if isinstance(val, UUID):
         
     | 
| 
      
 135 
     | 
    
         
            +
                        return val
         
     | 
| 
      
 136 
     | 
    
         
            +
                    return UUID(str(val))
         
     | 
| 
       217 
137 
     | 
    
         | 
| 
       218 
138 
     | 
    
         
             
                @field_serializer("id")
         
     | 
| 
       219 
     | 
    
         
            -
                def _serialize_id_type(self, val:  
     | 
| 
      
 139 
     | 
    
         
            +
                def _serialize_id_type(self, val: UUID) -> str:
         
     | 
| 
       220 
140 
     | 
    
         
             
                    """Serializes the `id` field to a string."""
         
     | 
| 
       221 
141 
     | 
    
         
             
                    return str(val)
         
     | 
| 
       222 
142 
     | 
    
         | 
| 
         @@ -328,28 +248,27 @@ class Element(BaseModel, Observable): 
     | 
|
| 
       328 
248 
     | 
    
         | 
| 
       329 
249 
     | 
    
         | 
| 
       330 
250 
     | 
    
         
             
            DEFAULT_ELEMENT_SERIALIZER = ln.get_orjson_default(
         
     | 
| 
       331 
     | 
    
         
            -
                order=[ 
     | 
| 
      
 251 
     | 
    
         
            +
                order=[Element, BaseModel],
         
     | 
| 
       332 
252 
     | 
    
         
             
                additional={
         
     | 
| 
       333 
     | 
    
         
            -
                    IDType: lambda o: str(o),
         
     | 
| 
       334 
253 
     | 
    
         
             
                    Element: lambda o: o.to_dict(),
         
     | 
| 
       335 
254 
     | 
    
         
             
                    BaseModel: lambda o: o.model_dump(mode="json"),
         
     | 
| 
       336 
255 
     | 
    
         
             
                },
         
     | 
| 
       337 
256 
     | 
    
         
             
            )
         
     | 
| 
       338 
257 
     | 
    
         | 
| 
       339 
258 
     | 
    
         | 
| 
       340 
     | 
    
         
            -
            def validate_order(order: Any) -> list[ 
     | 
| 
       341 
     | 
    
         
            -
                """Validates and flattens an ordering into a list of  
     | 
| 
      
 259 
     | 
    
         
            +
            def validate_order(order: Any) -> list[UUID]:
         
     | 
| 
      
 260 
     | 
    
         
            +
                """Validates and flattens an ordering into a list of UUID objects.
         
     | 
| 
       342 
261 
     | 
    
         | 
| 
       343 
262 
     | 
    
         
             
                This function accepts a variety of possible representations for ordering
         
     | 
| 
       344 
263 
     | 
    
         
             
                (e.g., a single Element, a list of Elements, a dictionary with ID keys,
         
     | 
| 
       345 
     | 
    
         
            -
                or a nested structure) and returns a flat list of  
     | 
| 
      
 264 
     | 
    
         
            +
                or a nested structure) and returns a flat list of UUID objects.
         
     | 
| 
       346 
265 
     | 
    
         | 
| 
       347 
266 
     | 
    
         
             
                Returns:
         
     | 
| 
       348 
     | 
    
         
            -
                    list[ 
     | 
| 
      
 267 
     | 
    
         
            +
                    list[UUID]: A flat list of validated UUID objects.
         
     | 
| 
       349 
268 
     | 
    
         | 
| 
       350 
269 
     | 
    
         
             
                Raises:
         
     | 
| 
       351 
270 
     | 
    
         
             
                    ValueError: If an invalid item is encountered or if there's a mixture
         
     | 
| 
       352 
     | 
    
         
            -
                        of types not all convertible to  
     | 
| 
      
 271 
     | 
    
         
            +
                        of types not all convertible to UUID.
         
     | 
| 
       353 
272 
     | 
    
         
             
                """
         
     | 
| 
       354 
273 
     | 
    
         
             
                if isinstance(order, Element):
         
     | 
| 
       355 
274 
     | 
    
         
             
                    return [order.id]
         
     | 
| 
         @@ -357,89 +276,79 @@ def validate_order(order: Any) -> list[IDType]: 
     | 
|
| 
       357 
276 
     | 
    
         
             
                    order = list(order.keys())
         
     | 
| 
       358 
277 
     | 
    
         | 
| 
       359 
278 
     | 
    
         
             
                stack = [order]
         
     | 
| 
       360 
     | 
    
         
            -
                out: list[ 
     | 
| 
      
 279 
     | 
    
         
            +
                out: list[UUID] = []
         
     | 
| 
       361 
280 
     | 
    
         
             
                while stack:
         
     | 
| 
       362 
281 
     | 
    
         
             
                    cur = stack.pop()
         
     | 
| 
       363 
282 
     | 
    
         
             
                    if cur is None:
         
     | 
| 
       364 
283 
     | 
    
         
             
                        continue
         
     | 
| 
       365 
284 
     | 
    
         
             
                    if isinstance(cur, Element):
         
     | 
| 
       366 
285 
     | 
    
         
             
                        out.append(cur.id)
         
     | 
| 
       367 
     | 
    
         
            -
                    elif isinstance(cur, IDType):
         
     | 
| 
       368 
     | 
    
         
            -
                        out.append(cur)
         
     | 
| 
       369 
286 
     | 
    
         
             
                    elif isinstance(cur, UUID):
         
     | 
| 
       370 
     | 
    
         
            -
                        out.append( 
     | 
| 
      
 287 
     | 
    
         
            +
                        out.append(cur)
         
     | 
| 
       371 
288 
     | 
    
         
             
                    elif isinstance(cur, str):
         
     | 
| 
       372 
     | 
    
         
            -
                        out.append( 
     | 
| 
      
 289 
     | 
    
         
            +
                        out.append(UUID(cur))
         
     | 
| 
       373 
290 
     | 
    
         
             
                    elif isinstance(cur, (list, tuple, set)):
         
     | 
| 
       374 
291 
     | 
    
         
             
                        stack.extend(reversed(cur))
         
     | 
| 
       375 
292 
     | 
    
         
             
                    else:
         
     | 
| 
       376 
293 
     | 
    
         
             
                        raise ValueError("Invalid item in order.")
         
     | 
| 
       377 
294 
     | 
    
         | 
| 
       378 
     | 
    
         
            -
                if not out 
     | 
| 
       379 
     | 
    
         
            -
                    return []
         
     | 
| 
       380 
     | 
    
         
            -
             
     | 
| 
       381 
     | 
    
         
            -
                # Check for consistent IDType usage
         
     | 
| 
       382 
     | 
    
         
            -
                first_type = type(out[0])
         
     | 
| 
       383 
     | 
    
         
            -
                if first_type is IDType:
         
     | 
| 
       384 
     | 
    
         
            -
                    for item in out:
         
     | 
| 
       385 
     | 
    
         
            -
                        if not isinstance(item, IDType):
         
     | 
| 
       386 
     | 
    
         
            -
                            raise ValueError("Mixed types in order.")
         
     | 
| 
       387 
     | 
    
         
            -
                    return out
         
     | 
| 
       388 
     | 
    
         
            -
                raise ValueError("Unrecognized type(s) in order.")
         
     | 
| 
      
 295 
     | 
    
         
            +
                return [] if not out else out
         
     | 
| 
       389 
296 
     | 
    
         | 
| 
       390 
297 
     | 
    
         | 
| 
       391 
298 
     | 
    
         
             
            E = TypeVar("E", bound=Element)
         
     | 
| 
       392 
299 
     | 
    
         | 
| 
       393 
300 
     | 
    
         | 
| 
       394 
301 
     | 
    
         
             
            class ID(Generic[E]):
         
     | 
| 
       395 
     | 
    
         
            -
                """Utility class for working with  
     | 
| 
      
 302 
     | 
    
         
            +
                """Utility class for working with UUID objects and Elements.
         
     | 
| 
       396 
303 
     | 
    
         | 
| 
       397 
304 
     | 
    
         
             
                This class provides helper methods to extract IDs from Elements, strings,
         
     | 
| 
       398 
305 
     | 
    
         
             
                or UUIDs, and to test whether a given object can be interpreted as
         
     | 
| 
       399 
306 
     | 
    
         
             
                an ID.
         
     | 
| 
       400 
307 
     | 
    
         
             
                """
         
     | 
| 
       401 
308 
     | 
    
         | 
| 
       402 
     | 
    
         
            -
                ID: TypeAlias =  
     | 
| 
      
 309 
     | 
    
         
            +
                ID: TypeAlias = UUID
         
     | 
| 
       403 
310 
     | 
    
         
             
                Item: TypeAlias = E | Element  # type: ignore
         
     | 
| 
       404 
     | 
    
         
            -
                Ref: TypeAlias =  
     | 
| 
       405 
     | 
    
         
            -
                IDSeq: TypeAlias = Sequence[ 
     | 
| 
      
 311 
     | 
    
         
            +
                Ref: TypeAlias = UUID | E | str  # type: ignore
         
     | 
| 
      
 312 
     | 
    
         
            +
                IDSeq: TypeAlias = Sequence[UUID] | Ordering[E]  # type: ignore
         
     | 
| 
       406 
313 
     | 
    
         
             
                ItemSeq: TypeAlias = Sequence[E] | Collective[E]  # type: ignore
         
     | 
| 
       407 
314 
     | 
    
         
             
                RefSeq: TypeAlias = ItemSeq | Sequence[Ref] | Ordering[E]  # type: ignore
         
     | 
| 
       408 
315 
     | 
    
         | 
| 
       409 
316 
     | 
    
         
             
                @staticmethod
         
     | 
| 
       410 
     | 
    
         
            -
                def get_id(item: E) ->  
     | 
| 
       411 
     | 
    
         
            -
                    """Retrieves an  
     | 
| 
      
 317 
     | 
    
         
            +
                def get_id(item: E) -> UUID:
         
     | 
| 
      
 318 
     | 
    
         
            +
                    """Retrieves an UUID from multiple possible item forms.
         
     | 
| 
       412 
319 
     | 
    
         | 
| 
       413 
320 
     | 
    
         
             
                    Acceptable item types include:
         
     | 
| 
       414 
321 
     | 
    
         
             
                    - Element: Uses its `id` attribute.
         
     | 
| 
       415 
     | 
    
         
            -
                    -  
     | 
| 
      
 322 
     | 
    
         
            +
                    - UUID: Returns it directly.
         
     | 
| 
       416 
323 
     | 
    
         
             
                    - UUID: Validates and wraps it.
         
     | 
| 
       417 
324 
     | 
    
         
             
                    - str: Interpreted as a UUID if possible.
         
     | 
| 
       418 
325 
     | 
    
         | 
| 
       419 
326 
     | 
    
         
             
                    Returns:
         
     | 
| 
       420 
     | 
    
         
            -
                         
     | 
| 
      
 327 
     | 
    
         
            +
                        UUID: The validated ID.
         
     | 
| 
       421 
328 
     | 
    
         | 
| 
       422 
329 
     | 
    
         
             
                    Raises:
         
     | 
| 
       423 
     | 
    
         
            -
                        ValueError: If the item cannot be converted to an  
     | 
| 
      
 330 
     | 
    
         
            +
                        ValueError: If the item cannot be converted to an UUID.
         
     | 
| 
       424 
331 
     | 
    
         
             
                    """
         
     | 
| 
      
 332 
     | 
    
         
            +
                    if isinstance(item, UUID):
         
     | 
| 
      
 333 
     | 
    
         
            +
                        return item
         
     | 
| 
       425 
334 
     | 
    
         
             
                    if isinstance(item, Element):
         
     | 
| 
       426 
335 
     | 
    
         
             
                        return item.id
         
     | 
| 
       427 
     | 
    
         
            -
                    if isinstance(item,  
     | 
| 
       428 
     | 
    
         
            -
                        return  
     | 
| 
      
 336 
     | 
    
         
            +
                    if isinstance(item, str):
         
     | 
| 
      
 337 
     | 
    
         
            +
                        return UUID(item)
         
     | 
| 
       429 
338 
     | 
    
         
             
                    raise ValueError("Cannot get ID from item.")
         
     | 
| 
       430 
339 
     | 
    
         | 
| 
       431 
340 
     | 
    
         
             
                @staticmethod
         
     | 
| 
       432 
341 
     | 
    
         
             
                def is_id(item: Any) -> bool:
         
     | 
| 
       433 
     | 
    
         
            -
                    """Checks if an item can be validated as an  
     | 
| 
      
 342 
     | 
    
         
            +
                    """Checks if an item can be validated as an UUID.
         
     | 
| 
       434 
343 
     | 
    
         | 
| 
       435 
344 
     | 
    
         
             
                    Returns:
         
     | 
| 
       436 
     | 
    
         
            -
                        bool: True if `item` is or can be validated as an  
     | 
| 
      
 345 
     | 
    
         
            +
                        bool: True if `item` is or can be validated as an UUID;
         
     | 
| 
       437 
346 
     | 
    
         
             
                            otherwise, False.
         
     | 
| 
       438 
347 
     | 
    
         
             
                    """
         
     | 
| 
       439 
348 
     | 
    
         
             
                    try:
         
     | 
| 
       440 
     | 
    
         
            -
                         
     | 
| 
      
 349 
     | 
    
         
            +
                        ID.get_id(item)  # type: ignore
         
     | 
| 
       441 
350 
     | 
    
         
             
                        return True
         
     | 
| 
       442 
     | 
    
         
            -
                    except  
     | 
| 
      
 351 
     | 
    
         
            +
                    except ValueError:
         
     | 
| 
       443 
352 
     | 
    
         
             
                        return False
         
     | 
| 
       444 
353 
     | 
    
         | 
| 
       445 
354 
     | 
    
         |