lionagi 0.6.1__py3-none-any.whl → 0.7.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. lionagi/libs/token_transform/__init__.py +0 -0
  2. lionagi/libs/token_transform/llmlingua.py +1 -0
  3. lionagi/libs/token_transform/perplexity.py +439 -0
  4. lionagi/libs/token_transform/synthlang.py +409 -0
  5. lionagi/operations/ReAct/ReAct.py +126 -0
  6. lionagi/operations/ReAct/utils.py +28 -0
  7. lionagi/operations/__init__.py +1 -9
  8. lionagi/operations/_act/act.py +73 -0
  9. lionagi/operations/chat/__init__.py +3 -0
  10. lionagi/operations/chat/chat.py +173 -0
  11. lionagi/operations/communicate/__init__.py +0 -0
  12. lionagi/operations/communicate/communicate.py +108 -0
  13. lionagi/operations/instruct/__init__.py +3 -0
  14. lionagi/operations/instruct/instruct.py +29 -0
  15. lionagi/operations/interpret/__init__.py +3 -0
  16. lionagi/operations/interpret/interpret.py +39 -0
  17. lionagi/operations/operate/__init__.py +3 -0
  18. lionagi/operations/operate/operate.py +194 -0
  19. lionagi/operations/parse/__init__.py +3 -0
  20. lionagi/operations/parse/parse.py +89 -0
  21. lionagi/operations/plan/plan.py +3 -3
  22. lionagi/operations/select/__init__.py +0 -4
  23. lionagi/operations/select/select.py +11 -30
  24. lionagi/operations/select/utils.py +13 -2
  25. lionagi/operations/translate/__init__.py +0 -0
  26. lionagi/operations/translate/translate.py +47 -0
  27. lionagi/operations/types.py +16 -0
  28. lionagi/operatives/action/manager.py +115 -93
  29. lionagi/operatives/action/request_response_model.py +31 -0
  30. lionagi/operatives/action/tool.py +50 -20
  31. lionagi/operatives/strategies/__init__.py +3 -0
  32. lionagi/protocols/_concepts.py +1 -1
  33. lionagi/protocols/adapters/adapter.py +25 -0
  34. lionagi/protocols/adapters/json_adapter.py +107 -27
  35. lionagi/protocols/adapters/pandas_/csv_adapter.py +55 -11
  36. lionagi/protocols/adapters/pandas_/excel_adapter.py +52 -10
  37. lionagi/protocols/adapters/pandas_/pd_dataframe_adapter.py +54 -4
  38. lionagi/protocols/adapters/pandas_/pd_series_adapter.py +40 -0
  39. lionagi/protocols/generic/element.py +1 -1
  40. lionagi/protocols/generic/pile.py +5 -8
  41. lionagi/protocols/graph/edge.py +1 -1
  42. lionagi/protocols/graph/graph.py +16 -8
  43. lionagi/protocols/graph/node.py +1 -1
  44. lionagi/protocols/mail/exchange.py +126 -15
  45. lionagi/protocols/mail/mail.py +33 -0
  46. lionagi/protocols/mail/mailbox.py +62 -0
  47. lionagi/protocols/mail/manager.py +97 -41
  48. lionagi/protocols/mail/package.py +57 -3
  49. lionagi/protocols/messages/action_request.py +77 -26
  50. lionagi/protocols/messages/action_response.py +55 -26
  51. lionagi/protocols/messages/assistant_response.py +50 -15
  52. lionagi/protocols/messages/base.py +36 -0
  53. lionagi/protocols/messages/instruction.py +175 -145
  54. lionagi/protocols/messages/manager.py +152 -56
  55. lionagi/protocols/messages/message.py +61 -25
  56. lionagi/protocols/messages/system.py +54 -19
  57. lionagi/service/imodel.py +24 -0
  58. lionagi/session/branch.py +1116 -939
  59. lionagi/utils.py +1 -0
  60. lionagi/version.py +1 -1
  61. {lionagi-0.6.1.dist-info → lionagi-0.7.1.dist-info}/METADATA +1 -1
  62. {lionagi-0.6.1.dist-info → lionagi-0.7.1.dist-info}/RECORD +75 -56
  63. lionagi/libs/compress/models.py +0 -66
  64. lionagi/libs/compress/utils.py +0 -69
  65. lionagi/operations/select/prompt.py +0 -5
  66. /lionagi/{libs/compress → operations/ReAct}/__init__.py +0 -0
  67. /lionagi/operations/{strategies → _act}/__init__.py +0 -0
  68. /lionagi/{operations → operatives}/strategies/base.py +0 -0
  69. /lionagi/{operations → operatives}/strategies/concurrent.py +0 -0
  70. /lionagi/{operations → operatives}/strategies/concurrent_chunk.py +0 -0
  71. /lionagi/{operations → operatives}/strategies/concurrent_sequential_chunk.py +0 -0
  72. /lionagi/{operations → operatives}/strategies/params.py +0 -0
  73. /lionagi/{operations → operatives}/strategies/sequential.py +0 -0
  74. /lionagi/{operations → operatives}/strategies/sequential_chunk.py +0 -0
  75. /lionagi/{operations → operatives}/strategies/sequential_concurrent_chunk.py +0 -0
  76. /lionagi/{operations → operatives}/strategies/utils.py +0 -0
  77. {lionagi-0.6.1.dist-info → lionagi-0.7.1.dist-info}/WHEEL +0 -0
  78. {lionagi-0.6.1.dist-info → lionagi-0.7.1.dist-info}/licenses/LICENSE +0 -0
@@ -2,56 +2,37 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
-
6
5
  from enum import Enum
7
- from typing import Any
6
+ from typing import TYPE_CHECKING, Any
8
7
 
9
- from pydantic import BaseModel, Field
8
+ from pydantic import BaseModel
10
9
 
11
10
  from lionagi.operatives.types import Instruct
12
- from lionagi.session.branch import Branch
13
-
14
- from .prompt import PROMPT
15
- from .utils import parse_selection, parse_to_representation
16
11
 
12
+ from .utils import SelectionModel
17
13
 
18
- class SelectionModel(BaseModel):
19
- """Model representing the selection output."""
20
-
21
- selected: list[Any] = Field(default_factory=list)
14
+ if TYPE_CHECKING:
15
+ from lionagi.session.branch import Branch
22
16
 
23
17
 
24
18
  async def select(
19
+ branch: "Branch",
25
20
  instruct: Instruct | dict[str, Any],
26
21
  choices: list[str] | type[Enum] | dict[str, Any],
27
22
  max_num_selections: int = 1,
28
- branch: Branch | None = None,
29
23
  branch_kwargs: dict[str, Any] | None = None,
30
24
  return_branch: bool = False,
31
25
  verbose: bool = False,
32
26
  **kwargs: Any,
33
- ) -> SelectionModel | tuple[SelectionModel, Branch]:
34
- """Perform a selection operation from given choices.
35
-
36
- Args:
37
- instruct: Instruction model or dictionary.
38
- choices: Options to select from.
39
- max_num_selections: Maximum selections allowed.
40
- branch: Existing branch or None to create a new one.
41
- branch_kwargs: Additional arguments for branch creation.
42
- return_branch: If True, return the branch with the selection.
43
- verbose: Whether to enable verbose output.
44
- **kwargs: Additional keyword arguments.
45
-
46
- Returns:
47
- A SelectionModel instance, optionally with the branch.
48
- """
27
+ ) -> SelectionModel | tuple[SelectionModel, "Branch"]:
49
28
  if verbose:
50
29
  print(f"Starting selection with up to {max_num_selections} choices.")
51
30
 
31
+ from .utils import SelectionModel, parse_selection, parse_to_representation
32
+
52
33
  branch = branch or Branch(**(branch_kwargs or {}))
53
34
  selections, contents = parse_to_representation(choices)
54
- prompt = PROMPT.format(
35
+ prompt = SelectionModel.PROMPT.format(
55
36
  max_num_selections=max_num_selections, choices=selections
56
37
  )
57
38
 
@@ -73,7 +54,7 @@ async def select(
73
54
  instruct["context"] = context
74
55
 
75
56
  response_model: SelectionModel = await branch.operate(
76
- operative_model=SelectionModel,
57
+ response_format=SelectionModel,
77
58
  **kwargs,
78
59
  **instruct,
79
60
  )
@@ -4,14 +4,25 @@
4
4
 
5
5
  import inspect
6
6
  from enum import Enum
7
- from typing import Any
7
+ from typing import Any, ClassVar
8
8
 
9
- from pydantic import BaseModel, JsonValue
9
+ from pydantic import BaseModel, Field, JsonValue
10
10
 
11
11
  from lionagi.libs.validate.string_similarity import string_similarity
12
12
  from lionagi.utils import is_same_dtype
13
13
 
14
14
 
15
+ # TODO: Make select a field to be added into a model, much like reason and action
16
+ class SelectionModel(BaseModel):
17
+ """Model representing the selection output."""
18
+
19
+ PROMPT: ClassVar[str] = (
20
+ "Please select up to {max_num_selections} items from the following list {choices}. Provide the selection(s) into appropriate field in format required, and no comments from you"
21
+ )
22
+
23
+ selected: list[Any] = Field(default_factory=list)
24
+
25
+
15
26
  def parse_to_representation(
16
27
  choices: Enum | dict | list | tuple | set,
17
28
  ) -> tuple[list[str], JsonValue]:
File without changes
@@ -0,0 +1,47 @@
1
+ from typing import TYPE_CHECKING, Literal
2
+
3
+ from lionagi.service.imodel import iModel
4
+
5
+ if TYPE_CHECKING:
6
+ from lionagi.session.branch import Branch
7
+
8
+
9
+ async def translate(
10
+ branch: "Branch",
11
+ text: str,
12
+ technique: Literal["SynthLang"] = "SynthLang",
13
+ technique_kwargs: dict = None,
14
+ compress: bool = False,
15
+ chat_model: iModel = None,
16
+ compress_model: iModel = None,
17
+ compression_ratio: float = 0.2,
18
+ compress_kwargs=None,
19
+ verbose: bool = True,
20
+ new_branch: bool = True,
21
+ **kwargs,
22
+ ):
23
+ if technique == "SynthLang":
24
+ from lionagi.libs.token_transform.synthlang import (
25
+ translate_to_synthlang,
26
+ )
27
+
28
+ if not technique_kwargs:
29
+ technique_kwargs = {}
30
+ if not technique_kwargs.get("template_name"):
31
+ technique_kwargs["template_name"] = "symbolic_systems"
32
+
33
+ technique_kwargs = {**technique_kwargs, **kwargs}
34
+
35
+ return await translate_to_synthlang(
36
+ text=text,
37
+ compress=compress,
38
+ chat_model=chat_model or branch.chat_model,
39
+ compress_model=compress_model,
40
+ compression_ratio=compression_ratio,
41
+ compress_kwargs=compress_kwargs,
42
+ verbose=verbose,
43
+ branch=branch if not new_branch else None,
44
+ **technique_kwargs,
45
+ )
46
+
47
+ raise ValueError(f"Technique {technique} is not supported.")
@@ -3,11 +3,27 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
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
6
12
  from .plan.plan import plan
13
+ from .ReAct.ReAct import ReAct
7
14
  from .select.select import select
15
+ from .translate.translate import translate
8
16
 
9
17
  __all__ = (
10
18
  "brainstorm",
11
19
  "plan",
12
20
  "select",
21
+ "chat",
22
+ "communicate",
23
+ "instruct",
24
+ "interpret",
25
+ "operate",
26
+ "parse",
27
+ "ReAct",
28
+ "translate",
13
29
  )
@@ -2,11 +2,16 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ """
6
+ Defines the `ActionManager` class, a specialized Manager that registers
7
+ `Tool` objects (or callables) for function invocation. It can match
8
+ incoming requests (ActionRequest) to a registered tool, then run it.
9
+ """
10
+
5
11
  from typing import Any
6
12
 
7
13
  from lionagi.protocols._concepts import Manager
8
- from lionagi.protocols.generic.event import EventStatus, Execution
9
- from lionagi.protocols.generic.log import Log
14
+ from lionagi.protocols.generic.event import Execution
10
15
  from lionagi.protocols.messages.action_request import ActionRequest
11
16
  from lionagi.utils import to_list
12
17
 
@@ -18,9 +23,22 @@ __all__ = ("ActionManager",)
18
23
 
19
24
 
20
25
  class ActionManager(Manager):
26
+ """
27
+ A manager that registers function-based tools and invokes them
28
+ when triggered by an ActionRequest. Tools can be registered
29
+ individually or in bulk, and each tool must have a unique name.
30
+ """
21
31
 
22
32
  def __init__(self, *args: FuncTool, **kwargs) -> None:
33
+ """
34
+ Create an ActionManager, optionally registering initial tools.
23
35
 
36
+ Args:
37
+ *args (FuncTool):
38
+ A variable number of tools or callables.
39
+ **kwargs:
40
+ Additional named arguments that are also considered tools.
41
+ """
24
42
  super().__init__()
25
43
  self.registry: dict[str, Tool] = {}
26
44
 
@@ -28,24 +46,19 @@ class ActionManager(Manager):
28
46
  if args:
29
47
  tools.extend(to_list(args, dropna=True, flatten=True))
30
48
  if kwargs:
31
- tools.extend(
32
- to_list(kwargs, dropna=True, flatten=True, use_values=True)
33
- )
49
+ tools.extend(to_list(kwargs.values(), dropna=True, flatten=True))
50
+
34
51
  self.register_tools(tools, update=True)
35
52
 
36
53
  def __contains__(self, tool: FuncToolRef) -> bool:
37
- """Check if a tool is registered in the registry.
38
-
39
- Supports checking by:
40
- - Tool object
41
- - Tool name (string)
42
- - Callable function
43
-
44
- Args:
45
- tool: The tool to check for registration.
54
+ """
55
+ Check if a tool is registered, by either:
56
+ - The Tool object itself,
57
+ - A string name for the function,
58
+ - Or the callable's __name__.
46
59
 
47
60
  Returns:
48
- bool: True if tool is registered, False otherwise.
61
+ bool: True if found, else False.
49
62
  """
50
63
  if isinstance(tool, Tool):
51
64
  return tool.function in self.registry
@@ -55,24 +68,21 @@ class ActionManager(Manager):
55
68
  return tool.__name__ in self.registry
56
69
  return False
57
70
 
58
- def register_tool(
59
- self,
60
- tool: FuncTool,
61
- update: bool = False,
62
- ) -> None:
63
- """Register a single tool in the registry.
64
-
65
- If the tool is a callable function, it is automatically converted
66
- to a Tool object. Existing tools can be updated if update=True.
71
+ def register_tool(self, tool: FuncTool, update: bool = False) -> None:
72
+ """
73
+ Register a single tool/callable in the manager.
67
74
 
68
75
  Args:
69
- tool: The tool to register (Tool object or callable).
70
- update: If True, update existing tool; if False, raise error.
76
+ tool (FuncTool):
77
+ A `Tool` object or a raw callable function.
78
+ update (bool):
79
+ If True, allow replacing an existing tool with the same name.
71
80
 
72
81
  Raises:
73
82
  ValueError: If tool already registered and update=False.
74
- TypeError: If tool is not a Tool object or callable.
83
+ TypeError: If `tool` is not a Tool or callable.
75
84
  """
85
+ # Check if tool already exists
76
86
  if not update and tool in self:
77
87
  name = None
78
88
  if isinstance(tool, Tool):
@@ -81,93 +91,93 @@ class ActionManager(Manager):
81
91
  name = tool.__name__
82
92
  raise ValueError(f"Tool {name} is already registered.")
83
93
 
94
+ # Convert raw callable to a Tool if needed
84
95
  if callable(tool):
85
96
  tool = Tool(func_callable=tool)
86
97
  if not isinstance(tool, Tool):
87
- raise TypeError("Please register a Tool object or callable.")
88
-
98
+ raise TypeError(
99
+ "Must provide a `Tool` object or a callable function."
100
+ )
89
101
  self.registry[tool.function] = tool
90
102
 
91
103
  def register_tools(
92
- self,
93
- tools: list[FuncTool] | FuncTool,
94
- update: bool = False,
104
+ self, tools: list[FuncTool] | FuncTool, update: bool = False
95
105
  ) -> None:
96
- """Register multiple tools in the registry.
97
-
98
- Handles both single tools and lists of tools. Each tool can be
99
- either a Tool object or a callable function.
106
+ """
107
+ Register multiple tools at once.
100
108
 
101
109
  Args:
102
- tools: Single tool or list of tools to register.
103
- update: If True, update existing tools; if False, raise error.
110
+ tools (list[FuncTool] | FuncTool):
111
+ A single or list of tools/callables.
112
+ update (bool):
113
+ If True, allow updating existing tools.
104
114
 
105
115
  Raises:
106
- ValueError: If any tool is already registered.
107
- TypeError: If any tool is not a Tool object or callable.
116
+ ValueError: If a duplicate tool is found and update=False.
117
+ TypeError: If any item is not a Tool or callable.
108
118
  """
109
119
  tools_list = tools if isinstance(tools, list) else [tools]
110
- [
111
- self.register_tool(tool, update=update)
112
- for tool in to_list(tools_list, dropna=True, flatten=True)
113
- ]
120
+ for t in tools_list:
121
+ self.register_tool(t, update=update)
114
122
 
115
123
  def match_tool(
116
- self, action_request: ActionRequest | ActionRequestModel
124
+ self, action_request: ActionRequest | ActionRequestModel | dict
117
125
  ) -> FunctionCalling:
118
- if not isinstance(action_request, ActionRequest | ActionRequestModel):
126
+ """
127
+ Convert an ActionRequest (or dict with "function"/"arguments")
128
+ into a `FunctionCalling` instance by finding the matching tool.
129
+
130
+ Raises:
131
+ TypeError: If `action_request` is an unsupported type.
132
+ ValueError: If no matching tool is found in the registry.
133
+
134
+ Returns:
135
+ FunctionCalling: The event object that can be invoked.
136
+ """
137
+ if not isinstance(
138
+ action_request, ActionRequest | ActionRequestModel | dict
139
+ ):
119
140
  raise TypeError(f"Unsupported type {type(action_request)}")
120
141
 
121
- tool = self.registry.get(action_request.function, None)
142
+ func, args = None, None
143
+ if isinstance(action_request, dict):
144
+ func = action_request["function"]
145
+ args = action_request["arguments"]
146
+ else:
147
+ func = action_request.function
148
+ args = action_request.arguments
149
+
150
+ tool = self.registry.get(func, None)
122
151
  if not isinstance(tool, Tool):
123
- raise ValueError(
124
- f"Function {action_request.function} is not registered."
125
- )
126
- return FunctionCalling(
127
- func_tool=tool, arguments=action_request.arguments
128
- )
152
+ raise ValueError(f"Function {func} is not registered.")
153
+
154
+ return FunctionCalling(func_tool=tool, arguments=args)
129
155
 
130
156
  async def invoke(
131
- self, func_call: ActionRequestModel | ActionRequest
132
- ) -> FunctionCalling | Execution | None:
133
- """Invoke a tool based on the provided function call.
157
+ self,
158
+ func_call: ActionRequestModel | ActionRequest,
159
+ ) -> FunctionCalling:
160
+ """
161
+ High-level API to parse and run a function call.
134
162
 
135
- 1. Matches function call to registered tool
136
- 2. Creates FunctionCalling instance
137
- 3. Invokes function with arguments
138
- 4. Logs execution details
139
- 5. Returns result
163
+ Steps:
164
+ 1) Convert `func_call` to FunctionCalling via `match_tool`.
165
+ 2) `invoke()` the resulting object.
166
+ 3) Return the `FunctionCalling`, which includes `execution`.
140
167
 
141
168
  Args:
142
- func_call: Function call specification in supported format.
143
- log_manager: Optional logger for execution tracking.
169
+ func_call: The action request model or ActionRequest object.
144
170
 
145
171
  Returns:
146
- Result of tool invocation after processing pipeline.
147
-
148
- Raises:
149
- ValueError: If function not registered or call format invalid.
150
- """
151
- try:
152
- function_calling = self.match_tool(func_call)
153
- except ValueError as e:
154
- return Log(
155
- content={
156
- "event_type": "function_call",
157
- "status": EventStatus.FAILED,
158
- "error": str(e),
159
- }
160
- )
172
+ `FunctionCalling` event after it completes execution.
173
+ """
174
+ function_calling = self.match_tool(func_call)
161
175
  await function_calling.invoke()
162
176
  return function_calling
163
177
 
164
178
  @property
165
179
  def schema_list(self) -> list[dict[str, Any]]:
166
- """List all tool schemas currently registered.
167
-
168
- Returns:
169
- List of OpenAI function schemas for all registered tools.
170
- """
180
+ """Return the list of JSON schemas for all registered tools."""
171
181
  return [tool.tool_schema for tool in self.registry.values()]
172
182
 
173
183
  def get_tool_schema(
@@ -176,20 +186,25 @@ class ActionManager(Manager):
176
186
  auto_register: bool = True,
177
187
  update: bool = False,
178
188
  ) -> dict:
179
- """Retrieve the schema for specific tools or all tools.
189
+ """
190
+ Retrieve schemas for a subset of tools or for all.
180
191
 
181
192
  Args:
182
- tools: Specification of which tools to retrieve schemas for.
183
- If True, return all tools. If False, return empty dict.
184
- Can also be a specific tool or list of tools.
185
- **kwargs: Additional keyword arguments to include in output.
193
+ tools (ToolRef):
194
+ - If True, return schema for all tools.
195
+ - If False, return an empty dict.
196
+ - If specific tool(s), returns only those schemas.
197
+ auto_register (bool):
198
+ If a tool (callable) is not yet in the registry, register if True.
199
+ update (bool):
200
+ If True, allow updating existing tools.
186
201
 
187
202
  Returns:
188
- Dictionary containing tool schemas and additional kwargs.
203
+ dict: e.g., {"tools": [list of schemas]}
189
204
 
190
205
  Raises:
191
- ValueError: If a specified tool is not registered.
192
- TypeError: If an unsupported tool type is provided.
206
+ ValueError: If requested tool is not found and auto_register=False.
207
+ TypeError: If tool specification is invalid.
193
208
  """
194
209
  if isinstance(tools, list | tuple) and len(tools) == 1:
195
210
  tools = tools[0]
@@ -208,10 +223,15 @@ class ActionManager(Manager):
208
223
  tool: Any,
209
224
  auto_register: bool = True,
210
225
  update: bool = False,
211
- ) -> dict[str, Any] | list[dict[str, Any]]:
226
+ ) -> list[dict[str, Any]] | dict[str, Any]:
227
+ """
228
+ Internal helper to handle retrieval or registration of a single or
229
+ multiple tools, returning their schema(s).
230
+ """
212
231
  if isinstance(tool, dict):
213
- return tool
232
+ return tool # Already a schema
214
233
  if callable(tool):
234
+ # Possibly unregistered function
215
235
  name = tool.__name__
216
236
  if name not in self.registry:
217
237
  if auto_register:
@@ -234,3 +254,5 @@ class ActionManager(Manager):
234
254
 
235
255
 
236
256
  __all__ = ["ActionManager"]
257
+
258
+ # File: lionagi/operatives/action/manager.py
@@ -2,6 +2,12 @@
2
2
  #
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
+ """
6
+ Defines Pydantic models for action requests and responses. They typically map
7
+ to conversation messages describing which function is called, with what arguments,
8
+ and any returned output.
9
+ """
10
+
5
11
  from typing import Any
6
12
 
7
13
  from pydantic import Field, field_validator
@@ -28,6 +34,10 @@ __all__ = (
28
34
 
29
35
 
30
36
  class ActionRequestModel(HashableModel):
37
+ """
38
+ Captures a single action request, typically from a user or system message.
39
+ Includes the name of the function and the arguments to be passed.
40
+ """
31
41
 
32
42
  function: str | None = Field(
33
43
  None,
@@ -43,6 +53,12 @@ class ActionRequestModel(HashableModel):
43
53
 
44
54
  @field_validator("arguments", mode="before")
45
55
  def validate_arguments(cls, value: Any) -> dict[str, Any]:
56
+ """
57
+ Coerce arguments into a dictionary if possible, recursively.
58
+
59
+ Raises:
60
+ ValueError if the data can't be coerced.
61
+ """
46
62
  return to_dict(
47
63
  value,
48
64
  fuzzy_parse=True,
@@ -52,10 +68,19 @@ class ActionRequestModel(HashableModel):
52
68
 
53
69
  @field_validator("function", mode="before")
54
70
  def validate_function(cls, value: str) -> str:
71
+ """
72
+ Ensure the function name is a valid non-empty string (if provided).
73
+ """
55
74
  return validate_nullable_string_field(cls, value, "function", False)
56
75
 
57
76
  @classmethod
58
77
  def create(cls, content: str):
78
+ """
79
+ Attempt to parse a string (usually from a conversation or JSON) into
80
+ one or more ActionRequestModel instances.
81
+
82
+ If no valid structure is found, returns an empty list.
83
+ """
59
84
  try:
60
85
  content = parse_action_request(content)
61
86
  if content:
@@ -75,6 +100,10 @@ ACTION_REQUESTS_FIELD = FieldModel(
75
100
 
76
101
 
77
102
  class ActionResponseModel(HashableModel):
103
+ """
104
+ Encapsulates a function's output after being called. Typically
105
+ references the original function name, arguments, and the result.
106
+ """
78
107
 
79
108
  function: str = Field(default_factory=str, title="Function")
80
109
  arguments: dict[str, Any] = Field(default_factory=dict)
@@ -88,3 +117,5 @@ ACTION_RESPONSES_FIELD = FieldModel(
88
117
  title="Actions",
89
118
  description="**do not fill**",
90
119
  )
120
+
121
+ # File: lionagi/operatives/action/request_response_model.py