lionagi 0.17.11__py3-none-any.whl → 0.18.1__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 (109) hide show
  1. lionagi/_errors.py +0 -5
  2. lionagi/fields.py +83 -0
  3. lionagi/libs/schema/minimal_yaml.py +98 -0
  4. lionagi/ln/__init__.py +3 -1
  5. lionagi/ln/concurrency/primitives.py +4 -4
  6. lionagi/ln/concurrency/task.py +1 -0
  7. lionagi/ln/types.py +32 -5
  8. lionagi/models/field_model.py +21 -4
  9. lionagi/models/hashable_model.py +2 -3
  10. lionagi/operations/ReAct/ReAct.py +475 -238
  11. lionagi/operations/ReAct/utils.py +3 -0
  12. lionagi/operations/act/act.py +206 -0
  13. lionagi/operations/builder.py +5 -7
  14. lionagi/operations/chat/chat.py +130 -114
  15. lionagi/operations/communicate/communicate.py +101 -42
  16. lionagi/operations/fields.py +380 -0
  17. lionagi/operations/flow.py +8 -10
  18. lionagi/operations/interpret/interpret.py +65 -20
  19. lionagi/operations/node.py +4 -4
  20. lionagi/operations/operate/operate.py +216 -108
  21. lionagi/{protocols/operatives → operations/operate}/operative.py +4 -5
  22. lionagi/{protocols/operatives → operations/operate}/step.py +34 -39
  23. lionagi/operations/parse/parse.py +170 -142
  24. lionagi/operations/select/select.py +79 -18
  25. lionagi/operations/select/utils.py +8 -2
  26. lionagi/operations/types.py +119 -23
  27. lionagi/protocols/action/manager.py +5 -6
  28. lionagi/protocols/contracts.py +2 -2
  29. lionagi/protocols/generic/__init__.py +22 -0
  30. lionagi/protocols/generic/element.py +36 -127
  31. lionagi/protocols/generic/log.py +3 -2
  32. lionagi/protocols/generic/pile.py +9 -10
  33. lionagi/protocols/generic/progression.py +23 -22
  34. lionagi/protocols/graph/edge.py +6 -5
  35. lionagi/protocols/ids.py +6 -49
  36. lionagi/protocols/messages/__init__.py +29 -0
  37. lionagi/protocols/messages/action_request.py +86 -184
  38. lionagi/protocols/messages/action_response.py +73 -131
  39. lionagi/protocols/messages/assistant_response.py +130 -159
  40. lionagi/protocols/messages/base.py +31 -22
  41. lionagi/protocols/messages/instruction.py +280 -625
  42. lionagi/protocols/messages/manager.py +112 -62
  43. lionagi/protocols/messages/message.py +87 -197
  44. lionagi/protocols/messages/system.py +52 -123
  45. lionagi/protocols/types.py +1 -13
  46. lionagi/service/connections/__init__.py +3 -0
  47. lionagi/service/connections/endpoint.py +0 -8
  48. lionagi/service/connections/providers/claude_code_cli.py +3 -2
  49. lionagi/service/connections/providers/oai_.py +29 -94
  50. lionagi/service/connections/providers/ollama_.py +3 -2
  51. lionagi/service/hooks/_types.py +1 -1
  52. lionagi/service/hooks/_utils.py +1 -1
  53. lionagi/service/hooks/hook_event.py +3 -8
  54. lionagi/service/hooks/hook_registry.py +5 -5
  55. lionagi/service/hooks/hooked_event.py +63 -3
  56. lionagi/service/imodel.py +24 -20
  57. lionagi/service/third_party/claude_code.py +3 -3
  58. lionagi/service/third_party/openai_models.py +435 -0
  59. lionagi/service/token_calculator.py +1 -94
  60. lionagi/session/branch.py +190 -400
  61. lionagi/session/session.py +8 -99
  62. lionagi/tools/file/reader.py +2 -2
  63. lionagi/version.py +1 -1
  64. {lionagi-0.17.11.dist-info → lionagi-0.18.1.dist-info}/METADATA +6 -6
  65. lionagi-0.18.1.dist-info/RECORD +164 -0
  66. lionagi/fields/__init__.py +0 -47
  67. lionagi/fields/action.py +0 -188
  68. lionagi/fields/base.py +0 -153
  69. lionagi/fields/code.py +0 -239
  70. lionagi/fields/file.py +0 -234
  71. lionagi/fields/instruct.py +0 -135
  72. lionagi/fields/reason.py +0 -55
  73. lionagi/fields/research.py +0 -52
  74. lionagi/operations/_act/act.py +0 -86
  75. lionagi/operations/brainstorm/__init__.py +0 -2
  76. lionagi/operations/brainstorm/brainstorm.py +0 -498
  77. lionagi/operations/brainstorm/prompt.py +0 -11
  78. lionagi/operations/instruct/__init__.py +0 -2
  79. lionagi/operations/instruct/instruct.py +0 -28
  80. lionagi/operations/plan/__init__.py +0 -6
  81. lionagi/operations/plan/plan.py +0 -386
  82. lionagi/operations/plan/prompt.py +0 -25
  83. lionagi/operations/utils.py +0 -45
  84. lionagi/protocols/forms/__init__.py +0 -2
  85. lionagi/protocols/forms/base.py +0 -85
  86. lionagi/protocols/forms/flow.py +0 -79
  87. lionagi/protocols/forms/form.py +0 -86
  88. lionagi/protocols/forms/report.py +0 -48
  89. lionagi/protocols/mail/__init__.py +0 -2
  90. lionagi/protocols/mail/exchange.py +0 -220
  91. lionagi/protocols/mail/mail.py +0 -51
  92. lionagi/protocols/mail/mailbox.py +0 -103
  93. lionagi/protocols/mail/manager.py +0 -218
  94. lionagi/protocols/mail/package.py +0 -101
  95. lionagi/protocols/messages/templates/README.md +0 -28
  96. lionagi/protocols/messages/templates/action_request.jinja2 +0 -5
  97. lionagi/protocols/messages/templates/action_response.jinja2 +0 -9
  98. lionagi/protocols/messages/templates/assistant_response.jinja2 +0 -6
  99. lionagi/protocols/messages/templates/instruction_message.jinja2 +0 -61
  100. lionagi/protocols/messages/templates/system_message.jinja2 +0 -11
  101. lionagi/protocols/messages/templates/tool_schemas.jinja2 +0 -7
  102. lionagi/protocols/operatives/__init__.py +0 -2
  103. lionagi/service/connections/providers/types.py +0 -28
  104. lionagi/service/third_party/openai_model_names.py +0 -198
  105. lionagi/service/types.py +0 -58
  106. lionagi-0.17.11.dist-info/RECORD +0 -199
  107. /lionagi/operations/{_act → act}/__init__.py +0 -0
  108. {lionagi-0.17.11.dist-info → lionagi-0.18.1.dist-info}/WHEEL +0 -0
  109. {lionagi-0.17.11.dist-info → lionagi-0.18.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,17 +1,23 @@
1
1
  # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
2
2
  # SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import logging
5
- from typing import TYPE_CHECKING
4
+ import warnings
5
+ from typing import TYPE_CHECKING, Any, Literal
6
6
 
7
+ from pydantic import JsonValue
8
+
9
+ from lionagi.ln.fuzzy import FuzzyMatchKeysParams
7
10
  from lionagi.ln.fuzzy._fuzzy_validate import fuzzy_validate_mapping
8
- from lionagi.utils import UNDEFINED
11
+ from lionagi.ln.types import Undefined
12
+
13
+ from ..types import ChatParam, ParseParam
9
14
 
10
15
  if TYPE_CHECKING:
16
+ from lionagi.protocols.messages.instruction import Instruction
11
17
  from lionagi.session.branch import Branch
12
18
 
13
19
 
14
- async def communicate(
20
+ def prepare_communicate_kw(
15
21
  branch: "Branch",
16
22
  instruction=None,
17
23
  *,
@@ -29,7 +35,7 @@ async def communicate(
29
35
  parse_model=None,
30
36
  skip_validation=False,
31
37
  images=None,
32
- image_detail="auto",
38
+ image_detail: Literal["low", "high", "auto"] = "auto",
33
39
  num_parse_retries=3,
34
40
  fuzzy_match_kwargs=None,
35
41
  clear_messages=False,
@@ -37,88 +43,141 @@ async def communicate(
37
43
  include_token_usage_to_model: bool = False,
38
44
  **kwargs,
39
45
  ):
46
+ # Handle deprecated parameters
40
47
  if operative_model:
41
- logging.warning(
42
- "operative_model is deprecated. Use response_format instead."
48
+ warnings.warn(
49
+ "Parameter 'operative_model' is deprecated. Use 'response_format' instead.",
50
+ DeprecationWarning,
51
+ stacklevel=2,
43
52
  )
53
+
44
54
  if (
45
55
  (operative_model and response_format)
46
56
  or (operative_model and request_model)
47
57
  or (response_format and request_model)
48
58
  ):
49
59
  raise ValueError(
50
- "Cannot specify both operative_model and response_format"
51
- "or operative_model and request_model as they are aliases"
60
+ "Cannot specify both operative_model and response_format "
61
+ "or operative_model and request_model as they are aliases "
52
62
  "for the same parameter."
53
63
  )
54
64
 
55
65
  response_format = response_format or operative_model or request_model
56
-
57
66
  imodel = imodel or chat_model or branch.chat_model
58
67
  parse_model = parse_model or branch.parse_model
59
68
 
60
- if clear_messages:
61
- branch.msgs.clear_messages()
62
-
63
69
  if num_parse_retries > 5:
64
- logging.warning(
65
- f"Are you sure you want to retry {num_parse_retries} "
66
- "times? lowering retry attempts to 5. Suggestion is under 3"
70
+ warnings.warn(
71
+ f"num_parse_retries={num_parse_retries} is high. Lowering to 5. Suggestion: <3",
72
+ UserWarning,
73
+ stacklevel=2,
67
74
  )
68
75
  num_parse_retries = 5
69
76
 
70
- ins, res = await branch.chat(
71
- instruction=instruction,
77
+ # Build contexts
78
+ chat_param = ChatParam(
72
79
  guidance=guidance,
73
80
  context=context,
74
- sender=sender,
75
- recipient=recipient,
81
+ sender=sender or branch.user or "user",
82
+ recipient=recipient or branch.id,
76
83
  response_format=response_format,
77
84
  progression=progression,
78
- imodel=imodel,
79
- images=images,
85
+ tool_schemas=[],
86
+ images=images or [],
80
87
  image_detail=image_detail,
81
- plain_content=plain_content,
82
- return_ins_res_message=True,
88
+ plain_content=plain_content or "",
83
89
  include_token_usage_to_model=include_token_usage_to_model,
84
- **kwargs,
90
+ imodel=imodel,
91
+ imodel_kw=kwargs,
92
+ )
93
+
94
+ parse_param = None
95
+ if response_format and not skip_validation:
96
+ from ..parse.parse import get_default_call
97
+
98
+ fuzzy_kw = fuzzy_match_kwargs or {}
99
+ handle_validation = fuzzy_kw.pop("handle_validation", "raise")
100
+
101
+ parse_param = ParseParam(
102
+ response_format=response_format,
103
+ fuzzy_match_params=(
104
+ FuzzyMatchKeysParams(**fuzzy_kw)
105
+ if fuzzy_kw
106
+ else FuzzyMatchKeysParams()
107
+ ),
108
+ handle_validation=handle_validation,
109
+ alcall_params=get_default_call().with_updates(
110
+ retry_attempts=num_parse_retries
111
+ ),
112
+ imodel=parse_model,
113
+ imodel_kw={},
114
+ )
115
+
116
+ return {
117
+ "instruction": instruction or "",
118
+ "chat_param": chat_param,
119
+ "parse_param": parse_param,
120
+ "clear_messages": clear_messages,
121
+ "skip_validation": skip_validation,
122
+ "request_fields": request_fields,
123
+ }
124
+
125
+
126
+ async def communicate(
127
+ branch: "Branch",
128
+ instruction: "JsonValue | Instruction",
129
+ chat_param: ChatParam,
130
+ parse_param: ParseParam | None = None,
131
+ clear_messages: bool = False,
132
+ skip_validation: bool = False,
133
+ request_fields: dict | None = None,
134
+ ) -> Any:
135
+ if clear_messages:
136
+ branch.msgs.clear_messages()
137
+
138
+ from ..chat.chat import chat
139
+
140
+ ins, res = await chat(
141
+ branch, instruction, chat_param, return_ins_res_message=True
85
142
  )
143
+
86
144
  branch.msgs.add_message(instruction=ins)
87
145
  branch.msgs.add_message(assistant_response=res)
88
146
 
89
147
  if skip_validation:
90
148
  return res.response
91
149
 
92
- if response_format is not None:
93
- # Default to raising errors unless explicitly set in fuzzy_match_kwargs
94
- parse_kwargs = {
95
- "handle_validation": "raise", # Default to raising errors
96
- **(fuzzy_match_kwargs or {}),
97
- }
150
+ # Handle response_format with parse
151
+ if parse_param and chat_param.response_format:
152
+ from lionagi.protocols.messages.assistant_response import (
153
+ AssistantResponse,
154
+ )
155
+
156
+ from ..parse.parse import parse
98
157
 
99
158
  try:
100
- return await branch.parse(
101
- text=res.response,
102
- request_type=response_format,
103
- max_retries=num_parse_retries,
104
- **parse_kwargs,
159
+ out, res2 = await parse(
160
+ branch, res.response, parse_param, return_res_message=True
105
161
  )
162
+ if res2 and isinstance(res2, AssistantResponse):
163
+ res.metadata["original_model_response"] = res.model_response
164
+ # model_response is read-only property - update metadata instead
165
+ res.metadata["model_response"] = res2.model_response
166
+ return out
106
167
  except ValueError as e:
107
168
  # Re-raise with more context
108
- logging.error(
109
- f"Failed to parse response '{res.response}' into {response_format}: {e}"
110
- )
111
169
  raise ValueError(
112
- f"Failed to parse model response into {response_format.__name__}: {e}"
170
+ f"Failed to parse model response into {chat_param.response_format}: {e}"
113
171
  ) from e
114
172
 
173
+ # Handle request_fields with fuzzy validation
115
174
  if request_fields is not None:
116
175
  _d = fuzzy_validate_mapping(
117
176
  res.response,
118
177
  request_fields,
119
178
  handle_unmatched="force",
120
- fill_value=UNDEFINED,
179
+ fill_value=Undefined,
121
180
  )
122
- return {k: v for k, v in _d.items() if v != UNDEFINED}
181
+ return {k: v for k, v in _d.items() if v != Undefined}
123
182
 
124
183
  return res.response
@@ -0,0 +1,380 @@
1
+ import re
2
+ from typing import Any, Literal
3
+
4
+ from pydantic import BaseModel, Field, JsonValue, field_validator
5
+
6
+ from lionagi.ln import extract_json, to_dict, to_list
7
+ from lionagi.ln.types import Unset
8
+ from lionagi.models import HashableModel
9
+
10
+ _DEFAULT_FIELDS = {}
11
+
12
+
13
+ class Instruct(HashableModel):
14
+ """Model for defining instruction parameters and execution requirements."""
15
+
16
+ instruction: str | None = Field(
17
+ None,
18
+ description=(
19
+ "A clear, actionable task definition. Specify:\n"
20
+ "1) The primary goal or objective\n"
21
+ "2) Key success criteria or constraints\n"
22
+ "\n"
23
+ "Guidelines:\n"
24
+ "- Start with a direct action verb (e.g., 'Analyze', 'Generate', 'Create')\n"
25
+ "- Include scope, boundaries, or constraints\n"
26
+ "- Provide success criteria if relevant\n"
27
+ "- For complex tasks, break them into logical steps"
28
+ ),
29
+ )
30
+
31
+ guidance: JsonValue | None = Field(
32
+ None,
33
+ description=(
34
+ "Strategic direction and constraints for executing the task. "
35
+ "Include:\n"
36
+ "1) Preferred methods or frameworks\n"
37
+ "2) Quality benchmarks (e.g., speed, clarity)\n"
38
+ "3) Resource or environmental constraints\n"
39
+ "4) Relevant compliance or standards\n"
40
+ "Use None if no special guidance."
41
+ ),
42
+ )
43
+
44
+ context: JsonValue | None = Field(
45
+ None,
46
+ description=(
47
+ "Background information and current-state data needed for the task. "
48
+ "Should be:\n"
49
+ "1) Directly relevant\n"
50
+ "2) Sufficient to perform the task\n"
51
+ "3) Free of extraneous detail\n"
52
+ "Include environment, prior outcomes, system states, or dependencies. "
53
+ "Use None if no additional context is needed."
54
+ ),
55
+ )
56
+
57
+ reason: bool | None = Field(
58
+ None,
59
+ description=(
60
+ "Include a thoughtful explanation of decisions, trade-offs, "
61
+ "and insights. Encourage deeper introspection on why certain "
62
+ "choices were made, potential alternatives, and how confidence "
63
+ "was shaped. If not needed, set to None."
64
+ ),
65
+ )
66
+ actions: bool | None = Field(
67
+ None,
68
+ description=(
69
+ "Controls execution mode. "
70
+ "True: Execute specified actions. "
71
+ "False: Analysis/recommendations only. "
72
+ "None: Contextual execution."
73
+ ),
74
+ )
75
+
76
+ action_strategy: Literal["sequential", "concurrent"] | None = Field(
77
+ None,
78
+ description="Action strategy to use for executing actions. Default "
79
+ "is 'concurrent'. Only provide for if actions are enabled.",
80
+ )
81
+
82
+ @field_validator("instruction", "guidance", "context", mode="before")
83
+ def _validate_instruction(cls, v):
84
+ from lionagi.libs.validate.common_field_validators import (
85
+ validate_nullable_jsonvalue_field,
86
+ )
87
+
88
+ return validate_nullable_jsonvalue_field(cls, v)
89
+
90
+ @field_validator("reason", "actions", mode="before")
91
+ def _validate_reason(cls, v):
92
+ from lionagi.libs.validate.common_field_validators import (
93
+ validate_boolean_field,
94
+ )
95
+
96
+ return validate_boolean_field(cls, v)
97
+
98
+ @field_validator("action_strategy", mode="before")
99
+ def _validate_action_strategy(cls, v):
100
+ if v not in ["batch", "sequential", "concurrent"]:
101
+ return "concurrent"
102
+ return v
103
+
104
+
105
+ class Reason(HashableModel):
106
+ title: str | None = None
107
+ content: str | None = None
108
+ confidence_score: float | None = Field(
109
+ None,
110
+ title="Confidence Score",
111
+ description=(
112
+ "Numeric confidence score (0.0 to 1.0, up to three decimals) indicating "
113
+ "how well you've met user expectations. Use this guide:\n"
114
+ " • 1.0: Highly confident\n"
115
+ " • 0.8-1.0: Reasonably sure\n"
116
+ " • 0.5-0.8: Re-check, refine or backtrack\n"
117
+ " • 0.0-0.5: Off track, stop"
118
+ ),
119
+ )
120
+
121
+ @field_validator("confidence_score", mode="before")
122
+ def _validate_confidence(cls, v):
123
+ if v is None:
124
+ return None
125
+ try:
126
+ from lionagi.libs.validate.to_num import to_num
127
+
128
+ return to_num(
129
+ v,
130
+ upper_bound=1,
131
+ lower_bound=0,
132
+ num_type=float,
133
+ precision=3,
134
+ )
135
+ except Exception:
136
+ return -1
137
+
138
+
139
+ class ActionRequestModel(HashableModel):
140
+ """
141
+ Captures a single action request, typically from a user or system message.
142
+ Includes the name of the function and the arguments to be passed.
143
+ """
144
+
145
+ function: str | None = Field(
146
+ None,
147
+ title="Function",
148
+ description=(
149
+ "Name of the function to call from the provided `tool_schemas`. "
150
+ "If no `tool_schemas` exist, set to None or leave blank. "
151
+ "Never invent new function names outside what's given."
152
+ ),
153
+ examples=["multiply", "create_user"],
154
+ )
155
+ arguments: dict[str, Any] | None = Field(
156
+ None,
157
+ title="Arguments",
158
+ description=(
159
+ "Dictionary of arguments for the chosen function. "
160
+ "Use only argument names/types defined in `tool_schemas`. "
161
+ "Never introduce extra argument names."
162
+ ),
163
+ )
164
+
165
+ @field_validator("arguments", mode="before")
166
+ def validate_arguments(cls, value: Any) -> dict[str, Any]:
167
+ """
168
+ Coerce arguments into a dictionary if possible, recursively.
169
+
170
+ Raises:
171
+ ValueError if the data can't be coerced.
172
+ """
173
+ return to_dict(
174
+ value,
175
+ fuzzy_parse=True,
176
+ recursive=True,
177
+ recursive_python_only=False,
178
+ )
179
+
180
+ @field_validator("function", mode="before")
181
+ def validate_function(cls, value: str) -> str:
182
+ """
183
+ Ensure the function name is a valid non-empty string (if provided).
184
+ """
185
+ from lionagi.libs.validate.common_field_validators import (
186
+ validate_nullable_string_field,
187
+ )
188
+
189
+ return validate_nullable_string_field(cls, value, "function", False)
190
+
191
+ @classmethod
192
+ def create(cls, content: str):
193
+ """
194
+ Attempt to parse a string (usually from a conversation or JSON) into
195
+ one or more ActionRequestModel instances.
196
+
197
+ If no valid structure is found, returns an empty list.
198
+ """
199
+
200
+ def parse_action_request(content: str | dict) -> list[dict]:
201
+
202
+ json_blocks = []
203
+
204
+ if isinstance(content, BaseModel):
205
+ json_blocks = [content.model_dump()]
206
+
207
+ elif isinstance(content, str):
208
+ json_blocks = extract_json(content, fuzzy_parse=True)
209
+ if not json_blocks:
210
+ pattern2 = r"```python\s*(.*?)\s*```"
211
+ _d = re.findall(pattern2, content, re.DOTALL)
212
+ json_blocks = [
213
+ extract_json(match, fuzzy_parse=True) for match in _d
214
+ ]
215
+ json_blocks = to_list(json_blocks, dropna=True)
216
+
217
+ print(json_blocks)
218
+
219
+ elif content and isinstance(content, dict):
220
+ json_blocks = [content]
221
+
222
+ if json_blocks and not isinstance(json_blocks, list):
223
+ json_blocks = [json_blocks]
224
+
225
+ out = []
226
+
227
+ for i in json_blocks:
228
+ j = {}
229
+ if isinstance(i, dict):
230
+ if "function" in i and isinstance(i["function"], dict):
231
+ if "name" in i["function"]:
232
+ i["function"] = i["function"]["name"]
233
+ for k, v in i.items():
234
+ k = (
235
+ k.replace("action_", "")
236
+ .replace("recipient_", "")
237
+ .replace("s", "")
238
+ )
239
+ if k in ["name", "function", "recipient"]:
240
+ j["function"] = v
241
+ elif k in ["parameter", "argument", "arg", "param"]:
242
+ j["arguments"] = to_dict(
243
+ v,
244
+ str_type="json",
245
+ fuzzy_parse=True,
246
+ suppress=True,
247
+ )
248
+ if (
249
+ j
250
+ and all(key in j for key in ["function", "arguments"])
251
+ and j["arguments"]
252
+ ):
253
+ out.append(j)
254
+
255
+ return out
256
+
257
+ try:
258
+ ctx = parse_action_request(content)
259
+ if ctx:
260
+ return [cls.model_validate(i) for i in ctx]
261
+ return []
262
+ except Exception:
263
+ return []
264
+
265
+
266
+ class ActionResponseModel(HashableModel):
267
+ """
268
+ Encapsulates a function's output after being called. Typically
269
+ references the original function name, arguments, and the result.
270
+ """
271
+
272
+ function: str = Field(default_factory=str, title="Function")
273
+ arguments: dict[str, Any] = Field(default_factory=dict)
274
+ output: Any = None
275
+
276
+
277
+ def get_default_field(
278
+ kind: Literal[
279
+ "action_requests",
280
+ "action_responses",
281
+ "action_required",
282
+ "instruct",
283
+ "reason",
284
+ ],
285
+ default: Any = Unset,
286
+ nullable: bool = True,
287
+ listable: bool = None,
288
+ ):
289
+ global _DEFAULT_FIELDS
290
+ key = (kind, str(default), nullable, listable)
291
+ if key not in _DEFAULT_FIELDS:
292
+ _DEFAULT_FIELDS[key] = _get_default_fields(
293
+ kind, default=default, nullable=nullable, listable=listable
294
+ )
295
+ return _DEFAULT_FIELDS[key]
296
+
297
+
298
+ def _get_default_fields(
299
+ kind: Literal[
300
+ "action_requests",
301
+ "action_responses",
302
+ "action_required",
303
+ "instruct",
304
+ "reason",
305
+ ],
306
+ default: Any = Unset,
307
+ nullable: bool = True,
308
+ listable: bool = None,
309
+ ):
310
+ from lionagi.models.field_model import FieldModel
311
+
312
+ fm = None
313
+
314
+ match kind:
315
+
316
+ case "instruct":
317
+ fm = FieldModel(Instruct, name="instruct_model")
318
+
319
+ case "action_required":
320
+ from lionagi.libs.validate.common_field_validators import (
321
+ validate_boolean_field,
322
+ )
323
+
324
+ fm = FieldModel(
325
+ bool,
326
+ name="action_required",
327
+ validator=lambda cls, v: validate_boolean_field(cls, v, False),
328
+ description=(
329
+ "Whether this step strictly requires performing actions. "
330
+ "If true, the requests in `action_requests` must be fulfilled, "
331
+ "assuming `tool_schemas` are available. "
332
+ "If false or no `tool_schemas` exist, actions are optional."
333
+ ),
334
+ )
335
+
336
+ case "action_requests":
337
+ fm = FieldModel(
338
+ ActionRequestModel,
339
+ name="action_requests",
340
+ listable=True,
341
+ description=(
342
+ "List of actions to be executed when `action_required` is true. "
343
+ "Each action must align with the available `tool_schemas`. "
344
+ "Leave empty if no actions are needed."
345
+ ),
346
+ )
347
+
348
+ case "action_responses":
349
+ fm = FieldModel(
350
+ ActionResponseModel, name="action_responses", listable=True
351
+ )
352
+
353
+ case "reason":
354
+ fm = FieldModel(Reason, name="reason")
355
+
356
+ case _:
357
+ raise ValueError(f"Unknown default field kind: {kind}")
358
+
359
+ if listable is not None:
360
+ if listable and not fm.is_listable:
361
+ fm = fm.as_listable()
362
+ else:
363
+ fm = fm.with_metadata("listable", False)
364
+
365
+ if nullable:
366
+ fm = fm.as_nullable()
367
+ default = None
368
+
369
+ if fm.is_listable and default is Unset:
370
+ default = list
371
+
372
+ if default is not Unset:
373
+ fm = fm.with_default(default)
374
+
375
+ if fm.is_listable:
376
+ fm = fm.with_validator(
377
+ lambda cls, x: to_list(x, dropna=True, flatten=True, unique=True)
378
+ )
379
+
380
+ return fm
@@ -10,8 +10,9 @@ using Events for synchronization and CapacityLimiter for concurrency control.
10
10
 
11
11
  import os
12
12
  from typing import TYPE_CHECKING, Any
13
+ from uuid import UUID
13
14
 
14
- from lionagi.ln._async_call import AlcallParams
15
+ from lionagi.ln import AlcallParams
15
16
  from lionagi.ln.concurrency import CapacityLimiter, ConcurrencyEvent
16
17
  from lionagi.operations.node import Operation
17
18
  from lionagi.protocols.types import EventStatus
@@ -164,13 +165,11 @@ class DependencyAwareExecutor:
164
165
  # Add to session branches collection directly
165
166
  # Check if this is a real branch (not a mock)
166
167
  try:
167
- from lionagi.protocols.types import IDType
168
-
169
168
  # Try to validate the ID
170
169
  if hasattr(branch_clone, "id"):
171
170
  branch_id = branch_clone.id
172
171
  # Only add to collections if it's a valid ID
173
- if isinstance(branch_id, (str, IDType)) or (
172
+ if isinstance(branch_id, (str, UUID)) or (
174
173
  hasattr(branch_id, "__str__")
175
174
  and not hasattr(branch_id, "_mock_name")
176
175
  ):
@@ -334,7 +333,7 @@ class DependencyAwareExecutor:
334
333
 
335
334
  # Wait for ALL sources (sources are now strings from builder.py)
336
335
  for source_id_str in sources:
337
- # Convert string back to IDType for lookup
336
+ # Convert string back to UUID for lookup
338
337
  # Check all operations to find matching ID
339
338
  for op_id in self.completion_events.keys():
340
339
  if str(op_id) == source_id_str:
@@ -367,7 +366,6 @@ class DependencyAwareExecutor:
367
366
  result, (str, int, float, bool)
368
367
  ):
369
368
  result = to_dict(result, recursive=True)
370
- # Use string representation of IDType for JSON serialization
371
369
  pred_context[f"{str(pred.id)}_result"] = result
372
370
 
373
371
  if "context" not in operation.parameters:
@@ -429,14 +427,14 @@ class DependencyAwareExecutor:
429
427
  if hasattr(branch, "_message_manager") and hasattr(
430
428
  primary_branch, "_message_manager"
431
429
  ):
432
- branch._message_manager.pile.clear()
433
- for msg in primary_branch._message_manager.pile:
430
+ branch._message_manager.messages.clear()
431
+ for msg in primary_branch._message_manager.messages:
434
432
  if hasattr(msg, "clone"):
435
- branch._message_manager.pile.append(
433
+ branch._message_manager.messages.append(
436
434
  msg.clone()
437
435
  )
438
436
  else:
439
- branch._message_manager.pile.append(msg)
437
+ branch._message_manager.messages.append(msg)
440
438
 
441
439
  # Clear the pending flag
442
440
  branch.metadata["pending_context_inheritance"] = False