lionagi 0.17.10__py3-none-any.whl → 0.18.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. lionagi/__init__.py +1 -2
  2. lionagi/_class_registry.py +1 -2
  3. lionagi/_errors.py +1 -2
  4. lionagi/adapters/async_postgres_adapter.py +2 -10
  5. lionagi/config.py +1 -2
  6. lionagi/fields/action.py +1 -2
  7. lionagi/fields/base.py +3 -0
  8. lionagi/fields/code.py +3 -0
  9. lionagi/fields/file.py +3 -0
  10. lionagi/fields/instruct.py +1 -2
  11. lionagi/fields/reason.py +1 -2
  12. lionagi/fields/research.py +3 -0
  13. lionagi/libs/__init__.py +1 -2
  14. lionagi/libs/file/__init__.py +1 -2
  15. lionagi/libs/file/chunk.py +1 -2
  16. lionagi/libs/file/process.py +1 -2
  17. lionagi/libs/schema/__init__.py +1 -2
  18. lionagi/libs/schema/as_readable.py +1 -2
  19. lionagi/libs/schema/extract_code_block.py +1 -2
  20. lionagi/libs/schema/extract_docstring.py +1 -2
  21. lionagi/libs/schema/function_to_schema.py +1 -2
  22. lionagi/libs/schema/load_pydantic_model_from_schema.py +1 -2
  23. lionagi/libs/schema/minimal_yaml.py +98 -0
  24. lionagi/libs/validate/__init__.py +1 -2
  25. lionagi/libs/validate/common_field_validators.py +1 -2
  26. lionagi/libs/validate/validate_boolean.py +1 -2
  27. lionagi/ln/fuzzy/_string_similarity.py +1 -2
  28. lionagi/ln/types.py +32 -5
  29. lionagi/models/__init__.py +1 -2
  30. lionagi/models/field_model.py +9 -1
  31. lionagi/models/hashable_model.py +4 -2
  32. lionagi/models/model_params.py +1 -2
  33. lionagi/models/operable_model.py +1 -2
  34. lionagi/models/schema_model.py +1 -2
  35. lionagi/operations/ReAct/ReAct.py +475 -239
  36. lionagi/operations/ReAct/__init__.py +1 -2
  37. lionagi/operations/ReAct/utils.py +4 -2
  38. lionagi/operations/__init__.py +1 -2
  39. lionagi/operations/act/__init__.py +2 -0
  40. lionagi/operations/act/act.py +206 -0
  41. lionagi/operations/brainstorm/__init__.py +1 -2
  42. lionagi/operations/brainstorm/brainstorm.py +1 -2
  43. lionagi/operations/brainstorm/prompt.py +1 -2
  44. lionagi/operations/builder.py +1 -2
  45. lionagi/operations/chat/__init__.py +1 -2
  46. lionagi/operations/chat/chat.py +131 -116
  47. lionagi/operations/communicate/communicate.py +102 -44
  48. lionagi/operations/flow.py +5 -6
  49. lionagi/operations/instruct/__init__.py +1 -2
  50. lionagi/operations/instruct/instruct.py +1 -2
  51. lionagi/operations/interpret/__init__.py +1 -2
  52. lionagi/operations/interpret/interpret.py +66 -22
  53. lionagi/operations/operate/__init__.py +1 -2
  54. lionagi/operations/operate/operate.py +213 -108
  55. lionagi/operations/parse/__init__.py +1 -2
  56. lionagi/operations/parse/parse.py +171 -144
  57. lionagi/operations/plan/__init__.py +1 -2
  58. lionagi/operations/plan/plan.py +1 -2
  59. lionagi/operations/plan/prompt.py +1 -2
  60. lionagi/operations/select/__init__.py +1 -2
  61. lionagi/operations/select/select.py +79 -19
  62. lionagi/operations/select/utils.py +2 -3
  63. lionagi/operations/types.py +120 -25
  64. lionagi/operations/utils.py +1 -2
  65. lionagi/protocols/__init__.py +1 -2
  66. lionagi/protocols/_concepts.py +1 -2
  67. lionagi/protocols/action/__init__.py +1 -2
  68. lionagi/protocols/action/function_calling.py +3 -20
  69. lionagi/protocols/action/manager.py +34 -4
  70. lionagi/protocols/action/tool.py +1 -2
  71. lionagi/protocols/contracts.py +1 -2
  72. lionagi/protocols/forms/__init__.py +1 -2
  73. lionagi/protocols/forms/base.py +1 -2
  74. lionagi/protocols/forms/flow.py +1 -2
  75. lionagi/protocols/forms/form.py +1 -2
  76. lionagi/protocols/forms/report.py +1 -2
  77. lionagi/protocols/generic/__init__.py +1 -2
  78. lionagi/protocols/generic/element.py +17 -65
  79. lionagi/protocols/generic/event.py +1 -2
  80. lionagi/protocols/generic/log.py +17 -14
  81. lionagi/protocols/generic/pile.py +3 -4
  82. lionagi/protocols/generic/processor.py +1 -2
  83. lionagi/protocols/generic/progression.py +1 -2
  84. lionagi/protocols/graph/__init__.py +1 -2
  85. lionagi/protocols/graph/edge.py +1 -2
  86. lionagi/protocols/graph/graph.py +1 -2
  87. lionagi/protocols/graph/node.py +1 -2
  88. lionagi/protocols/ids.py +1 -2
  89. lionagi/protocols/mail/__init__.py +1 -2
  90. lionagi/protocols/mail/exchange.py +1 -2
  91. lionagi/protocols/mail/mail.py +1 -2
  92. lionagi/protocols/mail/mailbox.py +1 -2
  93. lionagi/protocols/mail/manager.py +1 -2
  94. lionagi/protocols/mail/package.py +1 -2
  95. lionagi/protocols/messages/__init__.py +28 -2
  96. lionagi/protocols/messages/action_request.py +87 -186
  97. lionagi/protocols/messages/action_response.py +74 -133
  98. lionagi/protocols/messages/assistant_response.py +131 -161
  99. lionagi/protocols/messages/base.py +27 -20
  100. lionagi/protocols/messages/instruction.py +281 -626
  101. lionagi/protocols/messages/manager.py +113 -64
  102. lionagi/protocols/messages/message.py +88 -199
  103. lionagi/protocols/messages/system.py +53 -125
  104. lionagi/protocols/operatives/__init__.py +1 -2
  105. lionagi/protocols/operatives/operative.py +1 -2
  106. lionagi/protocols/operatives/step.py +1 -2
  107. lionagi/protocols/types.py +1 -4
  108. lionagi/service/connections/__init__.py +1 -2
  109. lionagi/service/connections/api_calling.py +1 -2
  110. lionagi/service/connections/endpoint.py +1 -10
  111. lionagi/service/connections/endpoint_config.py +1 -2
  112. lionagi/service/connections/header_factory.py +1 -2
  113. lionagi/service/connections/match_endpoint.py +1 -2
  114. lionagi/service/connections/mcp/__init__.py +1 -2
  115. lionagi/service/connections/mcp/wrapper.py +1 -2
  116. lionagi/service/connections/providers/__init__.py +1 -2
  117. lionagi/service/connections/providers/anthropic_.py +1 -2
  118. lionagi/service/connections/providers/claude_code_cli.py +1 -2
  119. lionagi/service/connections/providers/exa_.py +1 -2
  120. lionagi/service/connections/providers/nvidia_nim_.py +2 -27
  121. lionagi/service/connections/providers/oai_.py +30 -96
  122. lionagi/service/connections/providers/ollama_.py +4 -4
  123. lionagi/service/connections/providers/perplexity_.py +1 -2
  124. lionagi/service/hooks/__init__.py +1 -1
  125. lionagi/service/hooks/_types.py +1 -1
  126. lionagi/service/hooks/_utils.py +1 -1
  127. lionagi/service/hooks/hook_event.py +1 -1
  128. lionagi/service/hooks/hook_registry.py +1 -1
  129. lionagi/service/hooks/hooked_event.py +3 -4
  130. lionagi/service/imodel.py +1 -2
  131. lionagi/service/manager.py +1 -2
  132. lionagi/service/rate_limited_processor.py +1 -2
  133. lionagi/service/resilience.py +1 -2
  134. lionagi/service/third_party/anthropic_models.py +1 -2
  135. lionagi/service/third_party/claude_code.py +4 -4
  136. lionagi/service/third_party/openai_models.py +433 -0
  137. lionagi/service/token_calculator.py +1 -2
  138. lionagi/session/__init__.py +1 -2
  139. lionagi/session/branch.py +171 -180
  140. lionagi/session/session.py +4 -11
  141. lionagi/tools/__init__.py +1 -2
  142. lionagi/tools/base.py +1 -2
  143. lionagi/tools/file/__init__.py +1 -2
  144. lionagi/tools/file/reader.py +3 -4
  145. lionagi/tools/types.py +1 -2
  146. lionagi/utils.py +1 -2
  147. lionagi/version.py +1 -1
  148. {lionagi-0.17.10.dist-info → lionagi-0.18.0.dist-info}/METADATA +1 -2
  149. lionagi-0.18.0.dist-info/RECORD +191 -0
  150. lionagi/operations/_act/__init__.py +0 -3
  151. lionagi/operations/_act/act.py +0 -87
  152. lionagi/protocols/messages/templates/README.md +0 -28
  153. lionagi/protocols/messages/templates/action_request.jinja2 +0 -5
  154. lionagi/protocols/messages/templates/action_response.jinja2 +0 -9
  155. lionagi/protocols/messages/templates/assistant_response.jinja2 +0 -6
  156. lionagi/protocols/messages/templates/instruction_message.jinja2 +0 -61
  157. lionagi/protocols/messages/templates/system_message.jinja2 +0 -11
  158. lionagi/protocols/messages/templates/tool_schemas.jinja2 +0 -7
  159. lionagi/service/connections/providers/types.py +0 -28
  160. lionagi/service/third_party/openai_model_names.py +0 -198
  161. lionagi/service/types.py +0 -59
  162. lionagi-0.17.10.dist-info/RECORD +0 -199
  163. {lionagi-0.17.10.dist-info → lionagi-0.18.0.dist-info}/WHEEL +0 -0
  164. {lionagi-0.17.10.dist-info → lionagi-0.18.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,27 +1,122 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
- from .brainstorm.brainstorm import brainstorm
6
- from .chat.chat import chat
7
- from .communicate.communicate import communicate
8
- from .instruct.instruct import instruct
9
- from .interpret.interpret import interpret
10
- from .operate.operate import operate
11
- from .parse.parse import parse
12
- from .plan.plan import plan
13
- from .ReAct.ReAct import ReAct
14
- from .select.select import select
15
-
16
- __all__ = (
17
- "brainstorm",
18
- "plan",
19
- "select",
20
- "chat",
21
- "communicate",
22
- "instruct",
23
- "interpret",
24
- "operate",
25
- "parse",
26
- "ReAct",
27
- )
4
+ from dataclasses import dataclass
5
+ from enum import Enum
6
+ from typing import ClassVar, Literal
7
+
8
+ from pydantic import BaseModel, JsonValue
9
+
10
+ from lionagi.ln._async_call import AlcallParams
11
+ from lionagi.ln.fuzzy import FuzzyMatchKeysParams
12
+ from lionagi.ln.types import Params
13
+ from lionagi.protocols.action.tool import ToolRef
14
+ from lionagi.protocols.types import ID, SenderRecipient
15
+ from lionagi.service.imodel import iModel
16
+
17
+ HandleValidation = Literal["raise", "return_value", "return_none"]
18
+
19
+
20
+ class ContextPolicy(str, Enum):
21
+ """Policy for merging prompt context across morphism invocations.
22
+
23
+ Attributes:
24
+ REPLACE: New context completely replaces existing context
25
+ EXTEND: New context is appended to existing context
26
+ DEDUP: New context is appended but duplicates are removed
27
+ """
28
+
29
+ REPLACE = "replace"
30
+ EXTEND = "extend"
31
+ DEDUP = "dedup"
32
+
33
+
34
+ @dataclass(slots=True, frozen=True, init=False)
35
+ class MorphParam(Params):
36
+ """Base class for morphism parameters (invariants).
37
+
38
+ MorphParams represent the invariant properties that define a morphism
39
+ in LionAGI's categorical framework. They are frozen (immutable) and
40
+ hashable, enabling reproducible operations and efficient caching.
41
+
42
+ Morphisms are the fundamental abstraction in LionAGI - they represent
43
+ transformations between message states with well-defined parameters.
44
+ """
45
+
46
+ _none_as_sentinel: ClassVar[bool] = True
47
+
48
+
49
+ @dataclass(slots=True, frozen=True, init=False)
50
+ class ChatParam(MorphParam):
51
+ """Parameters for chat/communicate morphism.
52
+
53
+ Defines the invariant properties of a chat operation, including
54
+ guidance, context, response format, and LLM-visible content.
55
+
56
+ Note: 'context' field contains prompt context (LLM-visible facts).
57
+ This gets mapped to InstructionContent.prompt_context during message creation.
58
+ """
59
+
60
+ _none_as_sentinel: ClassVar[bool] = True
61
+ guidance: JsonValue = None
62
+ context: JsonValue = None
63
+ sender: SenderRecipient = None
64
+ recipient: SenderRecipient = None
65
+ response_format: type[BaseModel] | dict = None
66
+ progression: ID.RefSeq = None
67
+ tool_schemas: list[dict] = None
68
+ images: list = None
69
+ image_detail: Literal["low", "high", "auto"] = None
70
+ plain_content: str = None
71
+ include_token_usage_to_model: bool = False
72
+ imodel: iModel = None
73
+ imodel_kw: dict = None
74
+
75
+
76
+ @dataclass(slots=True, frozen=True, init=False)
77
+ class InterpretParam(MorphParam):
78
+ """Parameters for interpret morphism.
79
+
80
+ Defines interpretation style, domain, and sample writing for
81
+ transforming content according to specified guidelines.
82
+ """
83
+
84
+ _none_as_sentinel: ClassVar[bool] = True
85
+ domain: str = None
86
+ style: str = None
87
+ sample_writing: str = None
88
+ imodel: iModel = None
89
+ imodel_kw: dict = None
90
+
91
+
92
+ @dataclass(slots=True, frozen=True, init=False)
93
+ class ParseParam(MorphParam):
94
+ """Parameters for parse morphism.
95
+
96
+ Defines parsing behavior including response format validation,
97
+ fuzzy matching, and error handling strategies.
98
+ """
99
+
100
+ _none_as_sentinel: ClassVar[bool] = True
101
+ response_format: type[BaseModel] | dict = None
102
+ fuzzy_match_params: FuzzyMatchKeysParams | dict = None
103
+ handle_validation: HandleValidation = "raise"
104
+ alcall_params: AlcallParams | dict = None
105
+ imodel: iModel = None
106
+ imodel_kw: dict = None
107
+
108
+
109
+ @dataclass(slots=True, frozen=True, init=False)
110
+ class ActionParam(MorphParam):
111
+ """Parameters for action/tool execution morphism.
112
+
113
+ Defines tool execution strategy, error handling, and verbosity
114
+ for action-based operations.
115
+ """
116
+
117
+ _none_as_sentinel: ClassVar[bool] = True
118
+ action_call_params: AlcallParams = None
119
+ tools: ToolRef = None
120
+ strategy: Literal["concurrent", "sequential"] = "concurrent"
121
+ suppress_errors: bool = True
122
+ verbose_action: bool = False
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  from typing import TYPE_CHECKING
@@ -1,3 +1,2 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  from abc import ABC, abstractmethod
@@ -1,3 +1,2 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  import asyncio
@@ -116,29 +115,13 @@ class FunctionCalling(Event):
116
115
  self.execution.status = EventStatus.FAILED
117
116
  self.execution.error = str(e)
118
117
 
119
- def __str__(self) -> str:
120
- """Returns a string representation of the function call.
121
-
122
- Returns:
123
- A string in the format "function_name(arguments)".
124
- """
125
- return f"{self.func_tool.function}({self.arguments})"
126
-
127
- def __repr__(self) -> str:
128
- """Returns a detailed string representation of the function call.
129
-
130
- Returns:
131
- A string containing the class name and key attributes.
132
- """
133
- return f"FunctionCalling(function={self.func_tool.function}, arguments={self.arguments})"
134
-
135
- def to_dict(self) -> dict[str, Any]:
118
+ def to_dict(self, *args, **kw) -> dict[str, Any]:
136
119
  """Convert instance to dictionary.
137
120
 
138
121
  Returns:
139
122
  dict[str, Any]: Dictionary representation of the instance.
140
123
  """
141
- dict_ = super().to_dict()
124
+ dict_ = super().to_dict(*args, **kw)
142
125
  dict_["function"] = self.function
143
126
  dict_["arguments"] = self.arguments
144
127
  return dict_
@@ -1,7 +1,7 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
4
+ import logging
5
5
  from typing import Any
6
6
 
7
7
  from lionagi.fields.action import ActionRequestModel
@@ -348,16 +348,46 @@ class ActionManager(Manager):
348
348
  if request_options and tool.name in request_options:
349
349
  tool_request_options = request_options[tool.name]
350
350
 
351
+ # Extract schema from FastMCP tool and convert to lionagi format
352
+ tool_schema = None
351
353
  try:
352
- # Create tool with request_options for Pydantic validation
354
+ if (
355
+ hasattr(tool, "inputSchema")
356
+ and tool.inputSchema is not None
357
+ and isinstance(tool.inputSchema, dict)
358
+ ):
359
+ tool_schema = {
360
+ "type": "function",
361
+ "function": {
362
+ "name": tool.name,
363
+ "description": (
364
+ tool.description
365
+ if hasattr(tool, "description")
366
+ else None
367
+ ),
368
+ "parameters": tool.inputSchema,
369
+ },
370
+ }
371
+ except Exception as schema_error:
372
+ # If schema extraction fails, let Tool auto-generate from function signature
373
+ logging.warning(
374
+ f"Could not extract schema for {tool.name}: {schema_error}"
375
+ )
376
+ tool_schema = None
377
+
378
+ try:
379
+ # Create tool with auto-populated schema from MCP discovery
353
380
  tool_obj = Tool(
354
381
  mcp_config=mcp_config,
355
382
  request_options=tool_request_options,
383
+ tool_schema=tool_schema,
356
384
  )
357
385
  self.register_tool(tool_obj, update=update)
358
386
  registered_tools.append(tool.name)
359
387
  except Exception as e:
360
- print(f"Warning: Failed to register tool {tool.name}: {e}")
388
+ logging.warning(
389
+ f"Failed to register tool {tool.name}: {e}"
390
+ )
361
391
 
362
392
  return registered_tools
363
393
 
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
 
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  """V1 Observable Protocol for gradual evolution.
@@ -1,3 +1,2 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  from typing import Any, Literal
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  from pydantic import BaseModel, ConfigDict, Field
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  from typing import Any
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  from pydantic import Field
@@ -1,3 +1,2 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  from __future__ import annotations
@@ -48,22 +47,13 @@ class IDType:
48
47
  __slots__ = ("_id",)
49
48
 
50
49
  def __init__(self, id: UUID) -> None:
51
- """Initializes an IDType instance.
52
-
53
- Args:
54
- id (UUID): A UUID object (version 4 preferred).
55
- """
50
+ """Initializes an IDType instance."""
56
51
  self._id = id
57
52
 
58
53
  @classmethod
59
54
  def validate(cls, value: str | UUID | IDType) -> IDType:
60
55
  """Validates and converts a value into an IDType.
61
56
 
62
- Args:
63
- value (str | UUID | IDType):
64
- A string representing a UUID, a UUID instance, or another
65
- IDType instance.
66
-
67
57
  Returns:
68
58
  IDType: The validated IDType object.
69
59
 
@@ -105,9 +95,6 @@ class IDType:
105
95
  def __eq__(self, other: Any) -> bool:
106
96
  """Checks equality with another IDType based on UUID value.
107
97
 
108
- Args:
109
- other (Any): Another object for equality comparison.
110
-
111
98
  Returns:
112
99
  bool: True if both have the same underlying UUID; False otherwise.
113
100
  """
@@ -190,17 +177,7 @@ class Element(BaseModel, Observable):
190
177
  def _coerce_created_at(
191
178
  cls, val: float | dt.datetime | str | None
192
179
  ) -> float:
193
- """Coerces `created_at` to a float-based timestamp.
194
-
195
- Args:
196
- val (float | datetime | str | None): The initial creation time value.
197
-
198
- Returns:
199
- float: A float representing Unix epoch time in seconds.
200
-
201
- Raises:
202
- ValueError: If `val` cannot be converted to a float timestamp.
203
- """
180
+ """Coerces `created_at` to a float-based timestamp."""
204
181
  if val is None:
205
182
  return ln.now_utc().timestamp()
206
183
  if isinstance(val, float):
@@ -235,36 +212,17 @@ class Element(BaseModel, Observable):
235
212
 
236
213
  @field_validator("id", mode="before")
237
214
  def _ensure_idtype(cls, val: IDType | UUID | str) -> IDType:
238
- """Ensures `id` is validated as an IDType.
239
-
240
- Args:
241
- val (IDType | UUID | str):
242
- The incoming value for the `id` field.
243
-
244
- Returns:
245
- IDType: A validated IDType object.
246
- """
215
+ """Ensures `id` is validated as an IDType."""
247
216
  return IDType.validate(val)
248
217
 
249
218
  @field_serializer("id")
250
219
  def _serialize_id_type(self, val: IDType) -> str:
251
- """Serializes the `id` field to a string.
252
-
253
- Args:
254
- val (IDType): The IDType object to be serialized.
255
-
256
- Returns:
257
- str: The string representation of the UUID.
258
- """
220
+ """Serializes the `id` field to a string."""
259
221
  return str(val)
260
222
 
261
223
  @property
262
224
  def created_datetime(self) -> dt.datetime:
263
- """Returns the creation time as a datetime object.
264
-
265
- Returns:
266
- datetime: The creation time in UTC.
267
- """
225
+ """Returns the creation time as a datetime object."""
268
226
  return dt.datetime.fromtimestamp(self.created_at, tz=dt.timezone.utc)
269
227
 
270
228
  def __eq__(self, other: Any) -> bool:
@@ -294,23 +252,25 @@ class Element(BaseModel, Observable):
294
252
  return str(cls).split("'")[1]
295
253
  return cls.__name__
296
254
 
297
- def _to_dict(self) -> dict:
298
- dict_ = self.model_dump()
255
+ def _to_dict(self, **kw) -> dict:
256
+ """kw for model_dump."""
257
+ dict_ = self.model_dump(**kw)
299
258
  dict_["metadata"].update({"lion_class": self.class_name(full=True)})
300
259
  return {k: v for k, v in dict_.items() if ln.not_sentinel(v)}
301
260
 
302
261
  def to_dict(
303
- self, mode: Literal["python", "json", "db"] = "python"
262
+ self, mode: Literal["python", "json", "db"] = "python", **kw
304
263
  ) -> dict:
305
264
  """Converts this Element to a dictionary."""
306
265
  if mode == "python":
307
- return self._to_dict()
266
+ return self._to_dict(**kw)
308
267
  if mode == "json":
309
- return orjson.loads(self.to_json(decode=False))
268
+ return orjson.loads(self.to_json(decode=False, **kw))
310
269
  if mode == "db":
311
- dict_ = orjson.loads(self.to_json(decode=False))
270
+ dict_ = orjson.loads(self.to_json(decode=False, **kw))
312
271
  dict_["node_metadata"] = dict_.pop("metadata", {})
313
272
  return dict_
273
+ raise ValueError(f"Unsupported mode: {mode}")
314
274
 
315
275
  @classmethod
316
276
  def from_dict(cls, data: dict) -> Element:
@@ -353,9 +313,10 @@ class Element(BaseModel, Observable):
353
313
  data["metadata"] = metadata
354
314
  return cls.model_validate(data)
355
315
 
356
- def to_json(self, decode: bool = True) -> str:
316
+ def to_json(self, decode: bool = True, **kw) -> str:
357
317
  """Converts this Element to a JSON string."""
358
- dict_ = self._to_dict()
318
+ kw.pop("mode", None)
319
+ dict_ = self._to_dict(**kw)
359
320
  return ln.json_dumps(
360
321
  dict_, default=DEFAULT_ELEMENT_SERIALIZER, decode=decode
361
322
  )
@@ -383,9 +344,6 @@ def validate_order(order: Any) -> list[IDType]:
383
344
  (e.g., a single Element, a list of Elements, a dictionary with ID keys,
384
345
  or a nested structure) and returns a flat list of IDType objects.
385
346
 
386
- Args:
387
- order (Any): A potentially nested structure of items to be ordered.
388
-
389
347
  Returns:
390
348
  list[IDType]: A flat list of validated IDType objects.
391
349
 
@@ -458,9 +416,6 @@ class ID(Generic[E]):
458
416
  - UUID: Validates and wraps it.
459
417
  - str: Interpreted as a UUID if possible.
460
418
 
461
- Args:
462
- item (E): The item to convert to an ID.
463
-
464
419
  Returns:
465
420
  IDType: The validated ID.
466
421
 
@@ -477,9 +432,6 @@ class ID(Generic[E]):
477
432
  def is_id(item: Any) -> bool:
478
433
  """Checks if an item can be validated as an IDType.
479
434
 
480
- Args:
481
- item (Any): The object to check.
482
-
483
435
  Returns:
484
436
  bool: True if `item` is or can be validated as an IDType;
485
437
  otherwise, False.
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  from __future__ import annotations
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  from __future__ import annotations
@@ -11,6 +10,7 @@ from typing import Any
11
10
 
12
11
  from pydantic import BaseModel, Field, PrivateAttr, field_validator
13
12
 
13
+ from lionagi.models.hashable_model import HashableModel
14
14
  from lionagi.utils import create_path, to_dict
15
15
 
16
16
  from .element import Element
@@ -24,6 +24,8 @@ __all__ = (
24
24
  "LogManager",
25
25
  )
26
26
 
27
+ logger = logging.getLogger(__name__)
28
+
27
29
 
28
30
  class DataLoggerConfig(BaseModel):
29
31
  persist_dir: str | Path = "./data/logs"
@@ -88,13 +90,13 @@ class Log(Element):
88
90
  Create a new Log from an Element, storing a dict snapshot
89
91
  of the element's data.
90
92
  """
91
- if hasattr(content, "to_dict"):
92
- content = content.to_dict()
93
+ if isinstance(content, Element | HashableModel):
94
+ content = content.to_dict(mode="json")
93
95
  else:
94
96
  content = to_dict(content, recursive=True, suppress=True)
95
97
 
96
98
  if content is {}:
97
- logging.warning(
99
+ logger.warning(
98
100
  "No content to log, or original data was of invalid type. Making an empty log..."
99
101
  )
100
102
  return cls(content={"error": "No content to log."})
@@ -141,18 +143,19 @@ class DataLogger:
141
143
  if self._config.auto_save_on_exit:
142
144
  atexit.register(self.save_at_exit)
143
145
 
144
- def log(self, log_: Log) -> None:
146
+ def log(self, log_: Any) -> None:
145
147
  """
146
148
  Add a log synchronously. If capacity is reached, auto-dump to file.
147
149
  """
150
+ log_ = Log.create(log_) if not isinstance(log_, Log) else log_
148
151
  if self._config.capacity and len(self.logs) >= self._config.capacity:
149
152
  try:
150
153
  self.dump(clear=self._config.clear_after_dump)
151
154
  except Exception as e:
152
- logging.error(f"Failed to auto-dump logs: {e}")
155
+ logger.error(f"Failed to auto-dump logs: {e}")
153
156
  self.logs.include(log_)
154
157
 
155
- async def alog(self, log_: Log) -> None:
158
+ async def alog(self, log_: Any) -> None:
156
159
  """
157
160
  Add a log asynchronously. If capacity is reached, auto-dump to file.
158
161
  """
@@ -169,7 +172,7 @@ class DataLogger:
169
172
  unsupported, raise ValueError. Optionally clear logs after.
170
173
  """
171
174
  if not self.logs:
172
- logging.debug("No logs to dump.")
175
+ logger.debug("No logs to dump.")
173
176
  return
174
177
 
175
178
  fp = persist_path or self._create_path()
@@ -182,7 +185,7 @@ class DataLogger:
182
185
  else:
183
186
  raise ValueError(f"Unsupported file extension: {suffix}")
184
187
 
185
- logging.info(f"Dumped logs to {fp}")
188
+ logger.info(f"Dumped logs to {fp}")
186
189
  do_clear = (
187
190
  self._config.clear_after_dump if clear is None else clear
188
191
  )
@@ -191,12 +194,12 @@ class DataLogger:
191
194
  except Exception as e:
192
195
  # Check if it's a JSON serialization error with complex objects
193
196
  if "JSON serializable" in str(e):
194
- logging.debug(f"Could not serialize logs to JSON: {e}")
197
+ logger.debug(f"Could not serialize logs to JSON: {e}")
195
198
  # Don't raise for JSON serialization issues during dumps
196
199
  if clear is not False:
197
200
  self.logs.clear() # Still clear if requested
198
201
  else:
199
- logging.error(f"Failed to dump logs: {e}")
202
+ logger.error(f"Failed to dump logs: {e}")
200
203
  raise
201
204
 
202
205
  async def adump(
@@ -233,9 +236,9 @@ class DataLogger:
233
236
  # Only log debug level for JSON serialization errors during exit
234
237
  # These are non-critical and often occur with complex objects
235
238
  if "JSON serializable" in str(e):
236
- logging.debug(f"Could not serialize logs to JSON: {e}")
239
+ logger.debug(f"Could not serialize logs to JSON: {e}")
237
240
  else:
238
- logging.error(f"Failed to save logs on exit: {e}")
241
+ logger.error(f"Failed to save logs on exit: {e}")
239
242
 
240
243
  @classmethod
241
244
  def from_config(
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  from __future__ import annotations
@@ -222,13 +221,13 @@ class Pile(Element, Collective[T], Generic[T], Adaptable, AsyncAdaptable):
222
221
  "strict_type",
223
222
  }
224
223
 
225
- def __pydantic_extra__(self) -> dict[str, "FieldInfo"]:
224
+ def __pydantic_extra__(self) -> dict[str, FieldInfo]:
226
225
  return {
227
226
  "_lock": Field(default_factory=threading.Lock),
228
227
  "_async": Field(default_factory=ConcurrencyLock),
229
228
  }
230
229
 
231
- def __pydantic_private__(self) -> dict[str, "FieldInfo"]:
230
+ def __pydantic_private__(self) -> dict[str, FieldInfo]:
232
231
  return self.__pydantic_extra__()
233
232
 
234
233
  @classmethod
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  import asyncio
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  from __future__ import annotations
@@ -1,5 +1,4 @@
1
- # Copyright (c) 2023 - 2025, HaiyangLi <quantocean.li at gmail dot com>
2
- #
1
+ # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
3
2
  # SPDX-License-Identifier: Apache-2.0
4
3
 
5
4
  from .edge import Edge, EdgeCondition