lionagi 0.5.1__py3-none-any.whl → 0.5.2__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. lionagi/__init__.py +0 -1
  2. lionagi/core/action/tool.py +3 -5
  3. lionagi/core/communication/action_request.py +3 -3
  4. lionagi/core/communication/message.py +3 -3
  5. lionagi/core/communication/utils.py +3 -3
  6. lionagi/core/generic/component.py +4 -4
  7. lionagi/core/generic/element.py +51 -47
  8. lionagi/core/generic/graph.py +1 -1
  9. lionagi/core/generic/log.py +2 -2
  10. lionagi/core/generic/pile.py +10 -11
  11. lionagi/core/generic/progression.py +19 -12
  12. lionagi/core/generic/utils.py +6 -3
  13. lionagi/core/models/base.py +11 -68
  14. lionagi/core/models/field_model.py +42 -19
  15. lionagi/core/models/{new_model_params.py → model_params.py} +5 -6
  16. lionagi/core/models/note.py +2 -2
  17. lionagi/core/models/operable_model.py +8 -4
  18. lionagi/core/models/schema_model.py +9 -31
  19. lionagi/core/models/types.py +15 -6
  20. lionagi/core/session/branch.py +10 -7
  21. lionagi/core/session/branch_mixins.py +11 -12
  22. lionagi/core/session/session.py +1 -2
  23. lionagi/core/typing/__init__.py +4 -4
  24. lionagi/core/typing/{concepts.py → _concepts.py} +43 -2
  25. lionagi/core/typing/_id.py +104 -0
  26. lionagi/integrations/anthropic_/AnthropicModel.py +8 -3
  27. lionagi/integrations/groq_/GroqModel.py +11 -4
  28. lionagi/integrations/litellm_/imodel.py +6 -8
  29. lionagi/integrations/openai_/OpenAIModel.py +8 -3
  30. lionagi/integrations/openai_/image_token_calculator/image_token_calculator.py +14 -8
  31. lionagi/integrations/perplexity_/PerplexityModel.py +8 -3
  32. lionagi/libs/func/async_calls/__init__.py +6 -3
  33. lionagi/libs/func/async_calls/alcall.py +46 -0
  34. lionagi/libs/func/async_calls/bcall.py +49 -1
  35. lionagi/libs/func/async_calls/rcall.py +32 -0
  36. lionagi/libs/utils.py +12 -1
  37. lionagi/operations/brainstorm/brainstorm.py +3 -3
  38. lionagi/operations/plan/plan.py +3 -3
  39. lionagi/protocols/__init__.py +3 -0
  40. lionagi/protocols/configs/__init__.py +0 -15
  41. lionagi/protocols/configs/branch_config.py +1 -1
  42. lionagi/protocols/configs/imodel_config.py +2 -2
  43. lionagi/protocols/configs/log_config.py +1 -1
  44. lionagi/protocols/configs/types.py +15 -0
  45. lionagi/protocols/operatives/__init__.py +3 -15
  46. lionagi/protocols/operatives/action.py +4 -0
  47. lionagi/protocols/operatives/instruct.py +6 -2
  48. lionagi/protocols/operatives/operative.py +9 -21
  49. lionagi/protocols/operatives/prompts.py +4 -0
  50. lionagi/protocols/operatives/reason.py +4 -0
  51. lionagi/protocols/operatives/step.py +11 -23
  52. lionagi/protocols/operatives/types.py +19 -0
  53. lionagi/protocols/registries/__init__.py +3 -0
  54. lionagi/protocols/registries/_component_registry.py +4 -0
  55. lionagi/protocols/registries/_pile_registry.py +4 -0
  56. lionagi/service/__init__.py +3 -0
  57. lionagi/service/service_match_util.py +4 -4
  58. lionagi/settings.py +10 -18
  59. lionagi/strategies/base.py +4 -5
  60. lionagi/strategies/concurrent.py +4 -3
  61. lionagi/strategies/concurrent_chunk.py +3 -3
  62. lionagi/strategies/concurrent_sequential_chunk.py +3 -3
  63. lionagi/strategies/params.py +7 -4
  64. lionagi/version.py +1 -1
  65. {lionagi-0.5.1.dist-info → lionagi-0.5.2.dist-info}/METADATA +4 -2
  66. {lionagi-0.5.1.dist-info → lionagi-0.5.2.dist-info}/RECORD +71 -70
  67. lionagi/core/typing/config.py +0 -15
  68. lionagi/core/typing/id.py +0 -221
  69. /lionagi/core/typing/{pydantic_.py → _pydantic.py} +0 -0
  70. /lionagi/core/typing/{typing_.py → _typing.py} +0 -0
  71. /lionagi/integrations/{services.py → _services.py} +0 -0
  72. {lionagi-0.5.1.dist-info → lionagi-0.5.2.dist-info}/WHEEL +0 -0
  73. {lionagi-0.5.1.dist-info → lionagi-0.5.2.dist-info}/licenses/LICENSE +0 -0
lionagi/__init__.py CHANGED
@@ -5,7 +5,6 @@ import logging
5
5
  from dotenv import load_dotenv
6
6
 
7
7
  from .core.session.types import Branch
8
- from .integrations.litellm_.imodel import LiteiModel
9
8
  from .protocols.operatives.step import Step
10
9
  from .service import iModel
11
10
  from .settings import Settings
@@ -138,12 +138,10 @@ class Tool(Element):
138
138
  Returns:
139
139
  str: A detailed string representation of the Tool.
140
140
  """
141
- timestamp_str = datetime.fromtimestamp(self.timestamp).isoformat(
142
- timespec="minutes"
143
- )
141
+ timestamp_str = self.created_datetime.strftime("%Y-%m-%d %H:%M:%S")
144
142
  return (
145
- f"{self.class_name()}(ln_id={self.ln_id[:6]}.., "
146
- f"timestamp={timestamp_str}), "
143
+ f"{self.class_name()}(ln_id={str(self.ln_id)[:6]}.., "
144
+ f"created_timestamp={timestamp_str}), "
147
145
  f"schema_={json.dumps(self.schema_, indent=4)}"
148
146
  )
149
147
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  from typing_extensions import override
6
6
 
7
- from lionagi.core.typing import ID, Any, Callable, LnID, Note
7
+ from lionagi.core.typing import ID, Any, Callable, IDType, Note
8
8
  from lionagi.libs.parse import to_dict
9
9
  from lionagi.libs.utils import copy
10
10
 
@@ -107,12 +107,12 @@ class ActionRequest(RoledMessage):
107
107
  )
108
108
 
109
109
  @property
110
- def action_response_id(self) -> LnID | None:
110
+ def action_response_id(self) -> IDType | None:
111
111
  """
112
112
  Get the ID of the corresponding action response.
113
113
 
114
114
  Returns:
115
- LnID | None: The ID of the action response, or None if not responded
115
+ IDType | None: The ID of the action response, or None if not responded
116
116
  """
117
117
  return self.content.get("action_response_id", None)
118
118
 
@@ -58,7 +58,7 @@ class MessageField(str, Enum):
58
58
  - LION_CLASS: Class identifier for LION system
59
59
  - ROLE: Message role (system/user/assistant)
60
60
  - CONTENT: Message content
61
- - ln_id: Unique message identifier
61
+ - id: Unique message identifier
62
62
  - SENDER: Message sender
63
63
  - RECIPIENT: Message recipient
64
64
  - METADATA: Additional message metadata
@@ -68,7 +68,7 @@ class MessageField(str, Enum):
68
68
  LION_CLASS = "lion_class"
69
69
  ROLE = "role"
70
70
  CONTENT = "content"
71
- ln_id = "ln_id"
71
+ id = "id"
72
72
  SENDER = "sender"
73
73
  RECIPIENT = "recipient"
74
74
  METADATA = "metadata"
@@ -248,7 +248,7 @@ class RoledMessage(Component, BaseMail):
248
248
  if origin_obj and isinstance(origin_obj, Communicatable):
249
249
  info_dict = {
250
250
  "clone_from_info": {
251
- "original_ln_id": origin_obj.ln_id,
251
+ "original_id": origin_obj.id,
252
252
  "original_timestamp": origin_obj.timestamp,
253
253
  "original_sender": origin_obj.sender,
254
254
  "original_recipient": origin_obj.recipient,
@@ -8,8 +8,8 @@ from lionagi.core.typing import (
8
8
  Any,
9
9
  BaseModel,
10
10
  IDError,
11
+ IDType,
11
12
  Literal,
12
- LnID,
13
13
  Note,
14
14
  )
15
15
  from lionagi.integrations.pydantic_ import break_down_pydantic_annotation
@@ -229,7 +229,7 @@ def prepare_instruction_content(
229
229
 
230
230
  def validate_sender_recipient(
231
231
  value: Any, /
232
- ) -> LnID | Literal["system", "user", "N/A", "assistant"]:
232
+ ) -> IDType | Literal["system", "user", "N/A", "assistant"]:
233
233
  """
234
234
  Validate sender and recipient fields for mail-like communication.
235
235
 
@@ -237,7 +237,7 @@ def validate_sender_recipient(
237
237
  value: The value to validate
238
238
 
239
239
  Returns:
240
- Union[LnID, Literal]: Valid sender/recipient value
240
+ Union[IDType, Literal]: Valid sender/recipient value
241
241
 
242
242
  Raises:
243
243
  ValueError: If value is not a valid sender or recipient
@@ -289,8 +289,8 @@ class Component(Element, OperableModel):
289
289
 
290
290
  output_str = (
291
291
  f"{self.__class__.__name__}("
292
- f"ln_id={self.ln_id[:8]}..., "
293
- f"timestamp={str(self.created_datetime)[:-6]}, "
292
+ f"ln_id={str(self.ln_id)[:8]}..., "
293
+ f"created_timestamp={str(self.created_datetime)[:-6]}, "
294
294
  f"content='{content_preview}', "
295
295
  f"metadata_keys={list(self.metadata.keys())}, "
296
296
  )
@@ -340,8 +340,8 @@ class Component(Element, OperableModel):
340
340
 
341
341
  repr_str = (
342
342
  f"{self.class_name()}("
343
- f"ln_id={repr(self.ln_id)}, "
344
- f"timestamp={str(self.created_datetime)[:-6]}, "
343
+ f"ln_id=IDType({str(self.ln_id)}), "
344
+ f"created_timestamp={str(self.created_datetime)[:-6]}, "
345
345
  f"content={content_repr}, "
346
346
  f"metadata={truncate_dict(self.metadata.content)}, "
347
347
  )
@@ -4,6 +4,8 @@
4
4
 
5
5
  from datetime import datetime
6
6
 
7
+ from pydantic import AliasChoices, field_serializer
8
+
7
9
  from lionagi.core._class_registry import LION_CLASS_REGISTRY, get_class
8
10
  from lionagi.core.typing import (
9
11
  ID,
@@ -12,8 +14,7 @@ from lionagi.core.typing import (
12
14
  ConfigDict,
13
15
  Field,
14
16
  IDError,
15
- LnID,
16
- Note,
17
+ IDType,
17
18
  Observable,
18
19
  TypeVar,
19
20
  field_validator,
@@ -48,32 +49,45 @@ class Element(BaseModel, Observable):
48
49
  - String formatting
49
50
 
50
51
  Attributes:
51
- ln_id (LnID): Unique identifier for the element. Generated automatically
52
+ id (IDType): Unique identifier for the element. Generated automatically
52
53
  and immutable.
53
54
  timestamp (float): Creation timestamp as Unix timestamp. Generated
54
55
  automatically and immutable.
55
56
  """
56
57
 
57
- ln_id: LnID = Field(
58
+ model_config = ConfigDict(
59
+ extra="forbid",
60
+ arbitrary_types_allowed=True,
61
+ use_enum_values=True,
62
+ populate_by_name=True,
63
+ )
64
+
65
+ ln_id: IDType = Field(
58
66
  default_factory=ID.id,
59
67
  title="Lion ID",
60
68
  description="Unique identifier for the element",
61
69
  frozen=True,
62
70
  )
63
71
 
64
- timestamp: float = Field(
65
- default_factory=lambda: time(type_="timestamp"),
66
- title="Creation Timestamp",
72
+ created_timestamp: float = Field(
73
+ default_factory=lambda: time(
74
+ tz=Settings.Config.TIMEZONE, type_="timestamp"
75
+ ),
76
+ title="Creation Datetime",
67
77
  frozen=True,
68
- alias="created",
78
+ alias=AliasChoices("created_at", "timestamp"),
69
79
  )
70
80
 
71
- model_config = ConfigDict(
72
- extra="forbid",
73
- arbitrary_types_allowed=True,
74
- use_enum_values=True,
75
- populate_by_name=True,
76
- )
81
+ @property
82
+ def created_datetime(self) -> datetime:
83
+ """Get the creation timestamp as a Unix timestamp.
84
+
85
+ Returns:
86
+ float: The creation timestamp.
87
+ """
88
+ return datetime.fromtimestamp(
89
+ self.created_timestamp, tz=Settings.Config.TIMEZONE
90
+ )
77
91
 
78
92
  @classmethod
79
93
  def class_name(cls) -> str:
@@ -89,41 +103,39 @@ class Element(BaseModel, Observable):
89
103
  super().__pydantic_init_subclass__(**kwargs)
90
104
  LION_CLASS_REGISTRY[cls.__name__] = cls
91
105
 
92
- @property
93
- def created_datetime(self) -> datetime:
94
- """Get the creation datetime of the Element.
95
-
96
- Converts the Unix timestamp to a timezone-aware datetime object using the
97
- timezone specified in Settings.Config.TIMEZONE.
98
-
99
- Returns:
100
- datetime: A timezone-aware datetime object representing when the
101
- Element was created.
102
- """
103
- return datetime.fromtimestamp(
104
- self.timestamp, tz=Settings.Config.TIMEZONE
105
- )
106
+ @field_serializer("ln_id")
107
+ def _serialize_id(self, value: IDType) -> str:
108
+ return str(self.ln_id)
106
109
 
107
110
  @field_validator("ln_id", mode="before")
108
- def _validate_id(cls, value: ID.ID) -> str:
111
+ def _validate_id(cls, value: str | IDType) -> str:
109
112
  try:
110
113
  return ID.get_id(value)
111
114
  except Exception:
112
115
  raise IDError(f"Invalid lion id: {value}")
113
116
 
114
- @field_validator("timestamp", mode="before")
117
+ @field_validator("created_timestamp", mode="before")
115
118
  def _validate_timestamp(cls, value: Any) -> float:
116
- if isinstance(value, (int, float)):
117
- return float(value)
118
119
  if isinstance(value, datetime):
119
120
  return value.timestamp()
120
- try:
121
- if isinstance(value, str):
121
+
122
+ if isinstance(value, int | float):
123
+ return value
124
+
125
+ if isinstance(value, str):
126
+ try:
127
+ return datetime.fromisoformat(value).timestamp()
128
+ except Exception as e:
122
129
  try:
123
- return float(value)
130
+ value = float(value)
124
131
  except Exception:
125
- return datetime.fromisoformat(value).timestamp()
126
- raise ValueError
132
+ raise ValueError(
133
+ f"Invalid datetime string format: {value}"
134
+ ) from e
135
+
136
+ try:
137
+ return datetime.fromtimestamp(value, tz=Settings.Config.TIMEZONE)
138
+
127
139
  except Exception as e:
128
140
  raise ValueError(f"Invalid datetime string format: {value}") from e
129
141
 
@@ -170,8 +182,8 @@ class Element(BaseModel, Observable):
170
182
  def __str__(self) -> str:
171
183
  timestamp_str = self.created_datetime.isoformat(timespec="minutes")
172
184
  return (
173
- f"{self.class_name()}(ln_id={self.ln_id[:6]}.., "
174
- f"timestamp={timestamp_str})"
185
+ f"{self.class_name()}(ln_id={str(self.ln_id)[:6]}.., "
186
+ f"created_timestamp={timestamp_str})"
175
187
  )
176
188
 
177
189
  def __hash__(self) -> int:
@@ -183,13 +195,5 @@ class Element(BaseModel, Observable):
183
195
  def __len__(self) -> int:
184
196
  return 1
185
197
 
186
- def to_note(self) -> Note:
187
- """Convert Log to Note.
188
-
189
- Returns:
190
- Note representation of the log
191
- """
192
- return Note(**self.to_dict())
193
-
194
198
 
195
199
  __all__ = ["Element"]
@@ -281,7 +281,7 @@ class Graph(Node):
281
281
  node_info.update({"class_name": node.class_name()})
282
282
  if hasattr(node, "name"):
283
283
  node_info.update({"name": node.name})
284
- g.add_node(node.ln_id, **node_info)
284
+ g.add_node(str(node.ln_id), **node_info)
285
285
 
286
286
  for _edge in self.internal_edges:
287
287
  edge_info = _edge.to_dict()
@@ -72,7 +72,7 @@ class Log(Element):
72
72
  if "log_id" in data:
73
73
  data["ln_id"] = data.pop("log_id")
74
74
  if "log_timestamp" in data:
75
- data["timestamp"] = data.pop("log_timestamp")
75
+ data["created_timestamp"] = data.pop("log_timestamp")
76
76
  if "log_class" in data:
77
77
  data["lion_class"] = data.pop("log_class")
78
78
  return data
@@ -140,7 +140,7 @@ class Log(Element):
140
140
  # Convert standard fields to log_* fields
141
141
  dict_["log_id"] = dict_.pop("ln_id")
142
142
  dict_["log_class"] = dict_.pop("lion_class")
143
- dict_["log_timestamp"] = dict_.pop("timestamp")
143
+ dict_["log_timestamp"] = dict_.pop("created_timestamp")
144
144
 
145
145
  dict_ = to_dict(
146
146
  dict_,
@@ -26,6 +26,7 @@ from lionagi.core.typing import (
26
26
  TypeVar,
27
27
  field_serializer,
28
28
  )
29
+ from lionagi.core.typing._concepts import IDType
29
30
  from lionagi.libs.parse import is_same_dtype, to_list
30
31
  from lionagi.protocols.adapters.adapter import Adapter, AdapterRegistry
31
32
  from lionagi.protocols.registries._pile_registry import PileAdapterRegistry
@@ -38,6 +39,9 @@ T = TypeVar("T", bound=Element)
38
39
  D = TypeVar("D")
39
40
 
40
41
 
42
+ __all__ = ("Pile", "pile")
43
+
44
+
41
45
  def synchronized(func: Callable):
42
46
  @wraps(func)
43
47
  def wrapper(self: "Pile", *args, **kwargs):
@@ -72,7 +76,7 @@ class Pile(Element, Generic[T]):
72
76
  strict_type (bool): Whether to enforce strict type checking
73
77
  """
74
78
 
75
- pile_: dict[str, T] = Field(default_factory=dict)
79
+ pile_: dict[IDType, T] = Field(default_factory=dict)
76
80
  item_type: set[type[T]] | None = Field(
77
81
  default=None,
78
82
  description="Set of allowed types for items in the pile.",
@@ -120,8 +124,8 @@ class Pile(Element, Generic[T]):
120
124
  _config = {}
121
125
  if "ln_id" in kwargs:
122
126
  _config["ln_id"] = kwargs["ln_id"]
123
- if "created" in kwargs:
124
- _config["created"] = kwargs["created"]
127
+ if "created_timestamp" in kwargs:
128
+ _config["created_timestamp"] = kwargs["created_timestamp"]
125
129
 
126
130
  super().__init__(strict_type=strict_type, **_config)
127
131
  self.item_type = self._validate_item_type(item_type)
@@ -303,7 +307,7 @@ class Pile(Element, Generic[T]):
303
307
  """Get all items in order."""
304
308
  return [self.pile_[key] for key in self.progress]
305
309
 
306
- def items(self) -> Sequence[tuple[str, T]]:
310
+ def items(self) -> Sequence[tuple[IDType, T]]:
307
311
  """Get all (ID, item) pairs in order."""
308
312
  return [(key, self.pile_[key]) for key in self.progress]
309
313
 
@@ -619,7 +623,7 @@ class Pile(Element, Generic[T]):
619
623
  except Exception as e:
620
624
  raise ItemNotFoundError(f"index {key}. Error: {e}")
621
625
 
622
- elif isinstance(key, str):
626
+ elif isinstance(key, IDType):
623
627
  try:
624
628
  return self.pile_[key]
625
629
  except Exception as e:
@@ -724,7 +728,7 @@ class Pile(Element, Generic[T]):
724
728
  if isinstance(key, int | slice):
725
729
  try:
726
730
  pops = self.progress[key]
727
- pops = [pops] if isinstance(pops, str) else pops
731
+ pops = [pops] if isinstance(pops, IDType) else pops
728
732
  result = []
729
733
  for i in pops:
730
734
  self.progress.remove(i)
@@ -965,10 +969,6 @@ class Pile(Element, Generic[T]):
965
969
  """Save to CSV file."""
966
970
  self.adapt_to(".csv", fp=fp, **kwargs)
967
971
 
968
- def to_excel(self, fp: str | Path, **kwargs: Any) -> None:
969
- """Save to Excel file."""
970
- self.adapt_to(".xlsx", fp=fp, **kwargs)
971
-
972
972
 
973
973
  def pile(
974
974
  items: Any = None,
@@ -1013,5 +1013,4 @@ def pile(
1013
1013
  )
1014
1014
 
1015
1015
 
1016
- __all__ = [Pile, pile]
1017
1016
  # File: autoos/generic/pile.py
@@ -6,15 +6,18 @@ import contextlib
6
6
  from collections.abc import Iterator
7
7
  from typing import Any, Self
8
8
 
9
- from pydantic import field_validator
9
+ from pydantic import field_serializer, field_validator
10
10
  from typing_extensions import override
11
11
 
12
- from lionagi.core.typing import ID, Field, ItemNotFoundError, LnID, Ordering
12
+ from lionagi.core.typing import ID, Field, IDType, ItemNotFoundError, Ordering
13
+ from lionagi.core.typing._concepts import Observable
13
14
  from lionagi.libs.parse import to_list
14
15
 
15
16
  from .element import Element
16
17
  from .utils import to_list_type, validate_order
17
18
 
19
+ __all__ = ("Progression", "prog")
20
+
18
21
 
19
22
  class Progression(Element, Ordering):
20
23
  """A flexible, ordered sequence container for Lion IDs.
@@ -41,14 +44,18 @@ class Progression(Element, Ordering):
41
44
  description="The name of the progression.",
42
45
  )
43
46
 
44
- order: list[ID.ID] = Field(
47
+ order: list[IDType] = Field(
45
48
  default_factory=list,
46
49
  title="Order",
47
50
  description="The order of the progression.",
48
51
  )
49
52
 
53
+ @field_serializer("order")
54
+ def _serialize_order(self, value: list[IDType]) -> list[str]:
55
+ return [str(i) for i in self.order]
56
+
50
57
  @field_validator("order", mode="before")
51
- def _validate_order(cls, value: ID.RefSeq) -> list[LnID]:
58
+ def _validate_order(cls, value: ID.RefSeq) -> list[IDType]:
52
59
  if not value:
53
60
  return []
54
61
  try:
@@ -66,15 +73,15 @@ class Progression(Element, Ordering):
66
73
  check = False
67
74
  for i in item:
68
75
  check = False
69
- if isinstance(i, str):
76
+ if isinstance(i, IDType):
70
77
  check = i in self.order
71
- elif isinstance(i, Element):
78
+ elif isinstance(i, Observable):
72
79
  check = i.ln_id in self.order
73
80
  if not check:
74
81
  return False
75
82
  return check
76
83
 
77
- def __list__(self) -> list[LnID]:
84
+ def __list__(self) -> list[IDType]:
78
85
  """Return a copy of the order."""
79
86
  return self.order[:]
80
87
 
@@ -127,11 +134,11 @@ class Progression(Element, Ordering):
127
134
  """Delete an item or slice of items from the progression."""
128
135
  del self.order[key]
129
136
 
130
- def __iter__(self) -> Iterator[LnID]:
137
+ def __iter__(self) -> Iterator[IDType]:
131
138
  """Iterate over the items in the progression."""
132
139
  return iter(self.order)
133
140
 
134
- def __next__(self) -> LnID:
141
+ def __next__(self) -> IDType:
135
142
  """Return the next item in the progression."""
136
143
  try:
137
144
  return next(iter(self.order))
@@ -155,7 +162,7 @@ class Progression(Element, Ordering):
155
162
  item_ = validate_order(item)
156
163
  self.order.extend(item_)
157
164
 
158
- def pop(self, index: int = None, /) -> str:
165
+ def pop(self, index: int = None, /) -> IDType:
159
166
  """Remove and return an item from the progression.
160
167
 
161
168
  Args:
@@ -255,7 +262,7 @@ class Progression(Element, Ordering):
255
262
 
256
263
  raise ItemNotFoundError(f"{item}")
257
264
 
258
- def popleft(self) -> str:
265
+ def popleft(self) -> IDType:
259
266
  """Remove and return the leftmost item from the progression.
260
267
 
261
268
  Returns:
@@ -371,7 +378,7 @@ class Progression(Element, Ordering):
371
378
  return hash(self.ln_id)
372
379
 
373
380
 
374
- def progression(
381
+ def prog(
375
382
  order: ID.RefSeq = None,
376
383
  name: str = None,
377
384
  /,
@@ -7,6 +7,7 @@ from collections.abc import Generator
7
7
  from typing import Any, TypeVar
8
8
 
9
9
  from lionagi.core.typing import ID, IDError
10
+ from lionagi.core.typing._concepts import IDType, Observable
10
11
 
11
12
  from .element import Element
12
13
 
@@ -17,8 +18,10 @@ def to_list_type(value: Any, /) -> list[Any]:
17
18
  """Convert input to a list format"""
18
19
  if value is None:
19
20
  return []
21
+ if isinstance(value, IDType):
22
+ return [value]
20
23
  if isinstance(value, str):
21
- return [value] if ID.is_id(value) else []
24
+ return ID.get_id(value) if ID.is_id(value) else []
22
25
  if isinstance(value, Element):
23
26
  return [value]
24
27
  if hasattr(value, "values") and callable(value.values):
@@ -35,8 +38,8 @@ def validate_order(value: Any, /) -> list[str]:
35
38
  result = []
36
39
  for item in to_list_type(value):
37
40
  if isinstance(item, str) and ID.is_id(item):
38
- result.append(item)
39
- elif isinstance(item, Element):
41
+ result.append(ID.get_id(item))
42
+ elif isinstance(item, Observable):
40
43
  result.append(item.ln_id)
41
44
  else:
42
45
  id_ = ID.get_id(item)
@@ -2,84 +2,27 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
- from ..typing.pydantic_ import BaseModel
6
- from ..typing.typing_ import UNDEFINED, Any, Self
5
+ from ..typing._pydantic import BaseModel
6
+ from ..typing._typing import UNDEFINED, Any, Self
7
7
 
8
- common_config = {
9
- "populate_by_name": True,
10
- "arbitrary_types_allowed": True,
11
- "use_enum_values": True,
12
- }
8
+ __all__ = ("BaseAutoModel",)
13
9
 
14
10
 
15
11
  class BaseAutoModel(BaseModel):
16
- """Base model class with enhanced serialization capabilities.
12
+ """a hashable pydantic model interface with to_dict and from_dict method"""
17
13
 
18
- Extends Pydantic's BaseModel to provide:
19
- - Clean dictionary conversion with UNDEFINED handling
20
- - Nested model serialization
21
- - Hash generation based on model content
22
- - Validation rules
23
-
24
- Example:
25
- ```python
26
- class User(BaseAutoModel):
27
- name: str = Field(min_length=2)
28
- age: int | None = None
29
- settings: dict = Field(default_factory=dict)
30
-
31
- user = User(name="John", age=30)
32
- data = user.to_dict(clean=True) # Excludes UNDEFINED values
33
- ```
34
-
35
- Attributes:
36
- model_config: Default configuration for all instances
37
- - validate_default: True to validate default values
38
- - populate_by_name: True to allow field population by alias
39
- - arbitrary_types_allowed: True to allow any type
40
- - use_enum_values: True to use enum values in serialization
41
- """
42
-
43
- def to_dict(self, clean: bool = False) -> dict[str, Any]:
44
- """Convert model to dictionary, with optional cleaning.
45
-
46
- Args:
47
- clean: If True, exclude UNDEFINED values from output.
48
- If False, include all fields using model_dump().
49
-
50
- Returns:
51
- Dictionary representation of model with nested models serialized
52
- """
53
- if not clean:
54
- return self.model_dump()
14
+ def to_dict(self, **kwargs) -> dict[str, Any]:
15
+ """kwargs for pydantic model_dump()"""
55
16
  return {
56
17
  k: v
57
- for k, v in self.model_dump(exclude_unset=True).items()
18
+ for k, v in self.model_dump(**kwargs).items()
58
19
  if v is not UNDEFINED
59
20
  }
60
21
 
61
22
  @classmethod
62
- def from_dict(cls, data: dict) -> Self:
63
- """Create model instance from dictionary data.
64
-
65
- Args:
66
- data: Dictionary containing field values
67
-
68
- Returns:
69
- New model instance
70
-
71
- Raises:
72
- ValueError: If required fields are missing or validation fails
73
- """
74
- return cls.model_validate(data)
23
+ def from_dict(cls, data: dict, **kwargs) -> Self:
24
+ """kwargs for model_validate"""
25
+ return cls.model_validate(data, **kwargs)
75
26
 
76
27
  def __hash__(self) -> int:
77
- """Generate hash based on model's clean dictionary representation.
78
-
79
- Returns:
80
- Hash value that uniquely identifies the model's content
81
- """
82
- return hash(str(self.to_dict(True)))
83
-
84
-
85
- __all__ = ["BaseAutoModel"]
28
+ return hash(str(self.to_dict()))