lionagi 0.5.0__py3-none-any.whl → 0.5.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.
Files changed (79) 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 +52 -15
  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/anthropic_/AnthropicService.py +4 -0
  28. lionagi/integrations/groq_/GroqModel.py +11 -4
  29. lionagi/integrations/groq_/GroqService.py +4 -0
  30. lionagi/integrations/litellm_/imodel.py +10 -8
  31. lionagi/integrations/ollama_/OllamaService.py +4 -0
  32. lionagi/integrations/openai_/OpenAIModel.py +12 -3
  33. lionagi/integrations/openai_/image_token_calculator/image_token_calculator.py +14 -8
  34. lionagi/integrations/perplexity_/PerplexityModel.py +8 -3
  35. lionagi/integrations/perplexity_/PerplexityService.py +4 -0
  36. lionagi/libs/func/async_calls/__init__.py +6 -3
  37. lionagi/libs/func/async_calls/alcall.py +46 -0
  38. lionagi/libs/func/async_calls/bcall.py +49 -1
  39. lionagi/libs/func/async_calls/rcall.py +32 -0
  40. lionagi/libs/utils.py +12 -1
  41. lionagi/operations/brainstorm/brainstorm.py +3 -3
  42. lionagi/operations/plan/plan.py +3 -3
  43. lionagi/protocols/__init__.py +3 -0
  44. lionagi/protocols/configs/__init__.py +0 -15
  45. lionagi/protocols/configs/branch_config.py +1 -1
  46. lionagi/protocols/configs/imodel_config.py +2 -2
  47. lionagi/protocols/configs/log_config.py +1 -1
  48. lionagi/protocols/configs/types.py +15 -0
  49. lionagi/protocols/operatives/__init__.py +3 -15
  50. lionagi/protocols/operatives/action.py +4 -0
  51. lionagi/protocols/operatives/instruct.py +6 -2
  52. lionagi/protocols/operatives/operative.py +9 -21
  53. lionagi/protocols/operatives/prompts.py +4 -0
  54. lionagi/protocols/operatives/reason.py +4 -0
  55. lionagi/protocols/operatives/step.py +11 -23
  56. lionagi/protocols/operatives/types.py +19 -0
  57. lionagi/protocols/registries/__init__.py +3 -0
  58. lionagi/protocols/registries/_component_registry.py +4 -0
  59. lionagi/protocols/registries/_pile_registry.py +4 -0
  60. lionagi/service/__init__.py +3 -0
  61. lionagi/service/imodel.py +22 -7
  62. lionagi/service/service.py +4 -0
  63. lionagi/service/service_match_util.py +4 -4
  64. lionagi/settings.py +10 -18
  65. lionagi/strategies/base.py +4 -5
  66. lionagi/strategies/concurrent.py +4 -3
  67. lionagi/strategies/concurrent_chunk.py +3 -3
  68. lionagi/strategies/concurrent_sequential_chunk.py +3 -3
  69. lionagi/strategies/params.py +7 -4
  70. lionagi/version.py +1 -1
  71. {lionagi-0.5.0.dist-info → lionagi-0.5.2.dist-info}/METADATA +214 -15
  72. {lionagi-0.5.0.dist-info → lionagi-0.5.2.dist-info}/RECORD +77 -76
  73. lionagi/core/typing/config.py +0 -15
  74. lionagi/core/typing/id.py +0 -221
  75. /lionagi/core/typing/{pydantic_.py → _pydantic.py} +0 -0
  76. /lionagi/core/typing/{typing_.py → _typing.py} +0 -0
  77. /lionagi/integrations/{services.py → _services.py} +0 -0
  78. {lionagi-0.5.0.dist-info → lionagi-0.5.2.dist-info}/WHEEL +0 -0
  79. {lionagi-0.5.0.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()))