lionagi 0.7.0__py3-none-any.whl → 0.7.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.
- lionagi/operations/ReAct/ReAct.py +2 -2
- lionagi/operations/_act/act.py +10 -3
- lionagi/operations/communicate/communicate.py +0 -59
- lionagi/operations/interpret/interpret.py +1 -2
- lionagi/operations/operate/operate.py +10 -5
- lionagi/operations/parse/parse.py +0 -36
- lionagi/operations/plan/plan.py +3 -3
- lionagi/operatives/action/manager.py +105 -82
- lionagi/operatives/action/request_response_model.py +31 -0
- lionagi/operatives/action/tool.py +50 -20
- lionagi/protocols/_concepts.py +1 -1
- lionagi/protocols/adapters/adapter.py +25 -0
- lionagi/protocols/adapters/json_adapter.py +107 -27
- lionagi/protocols/adapters/pandas_/csv_adapter.py +55 -11
- lionagi/protocols/adapters/pandas_/excel_adapter.py +52 -10
- lionagi/protocols/adapters/pandas_/pd_dataframe_adapter.py +54 -4
- lionagi/protocols/adapters/pandas_/pd_series_adapter.py +40 -0
- lionagi/protocols/generic/element.py +1 -1
- lionagi/protocols/generic/pile.py +5 -8
- lionagi/protocols/graph/edge.py +1 -1
- lionagi/protocols/graph/graph.py +16 -8
- lionagi/protocols/graph/node.py +1 -1
- lionagi/protocols/mail/exchange.py +126 -15
- lionagi/protocols/mail/mail.py +33 -0
- lionagi/protocols/mail/mailbox.py +62 -0
- lionagi/protocols/mail/manager.py +97 -41
- lionagi/protocols/mail/package.py +57 -3
- lionagi/protocols/messages/action_request.py +77 -26
- lionagi/protocols/messages/action_response.py +55 -26
- lionagi/protocols/messages/assistant_response.py +50 -15
- lionagi/protocols/messages/base.py +36 -0
- lionagi/protocols/messages/instruction.py +175 -145
- lionagi/protocols/messages/manager.py +152 -56
- lionagi/protocols/messages/message.py +61 -25
- lionagi/protocols/messages/system.py +54 -19
- lionagi/service/imodel.py +24 -0
- lionagi/session/branch.py +40 -32
- lionagi/utils.py +1 -0
- lionagi/version.py +1 -1
- {lionagi-0.7.0.dist-info → lionagi-0.7.2.dist-info}/METADATA +1 -1
- {lionagi-0.7.0.dist-info → lionagi-0.7.2.dist-info}/RECORD +43 -43
- {lionagi-0.7.0.dist-info → lionagi-0.7.2.dist-info}/WHEEL +0 -0
- {lionagi-0.7.0.dist-info → lionagi-0.7.2.dist-info}/licenses/LICENSE +0 -0
@@ -21,7 +21,7 @@ async def ReAct(
|
|
21
21
|
interpret: bool = False,
|
22
22
|
tools: Any = None,
|
23
23
|
tool_schemas: Any = None,
|
24
|
-
response_format: type[BaseModel] = None,
|
24
|
+
response_format: type[BaseModel] | BaseModel = None,
|
25
25
|
extension_allowed: bool = False,
|
26
26
|
max_extensions: int | None = None,
|
27
27
|
response_kwargs: dict | None = None,
|
@@ -60,7 +60,7 @@ async def ReAct(
|
|
60
60
|
kwargs_for_operate["actions"] = True
|
61
61
|
kwargs_for_operate["reason"] = True
|
62
62
|
|
63
|
-
# We'll pass the refined instruct_dict plus the user
|
63
|
+
# We'll pass the refined instruct_dict plus the user's other kwargs
|
64
64
|
from .utils import ReActAnalysis
|
65
65
|
|
66
66
|
# Step 1: Generate initial ReAct analysis
|
lionagi/operations/_act/act.py
CHANGED
@@ -7,9 +7,10 @@ from typing import TYPE_CHECKING
|
|
7
7
|
|
8
8
|
from pydantic import BaseModel
|
9
9
|
|
10
|
-
from lionagi.protocols.types import
|
10
|
+
from lionagi.protocols.types import Log
|
11
11
|
|
12
12
|
if TYPE_CHECKING:
|
13
|
+
from lionagi.operatives.types import ActionResponseModel
|
13
14
|
from lionagi.session.branch import Branch
|
14
15
|
|
15
16
|
|
@@ -17,7 +18,7 @@ async def _act(
|
|
17
18
|
branch: "Branch",
|
18
19
|
action_request: BaseModel | dict,
|
19
20
|
suppress_errors: bool = False,
|
20
|
-
) ->
|
21
|
+
) -> "ActionResponseModel":
|
21
22
|
|
22
23
|
_request = {}
|
23
24
|
|
@@ -35,7 +36,13 @@ async def _act(
|
|
35
36
|
try:
|
36
37
|
func_call = await branch._action_manager.invoke(_request)
|
37
38
|
except Exception as e:
|
38
|
-
|
39
|
+
content = {
|
40
|
+
"error": str(e),
|
41
|
+
"function": _request.get("function"),
|
42
|
+
"arguments": _request.get("arguments"),
|
43
|
+
"branch": str(branch.id),
|
44
|
+
}
|
45
|
+
branch._log_manager.log(Log(content=content))
|
39
46
|
if suppress_errors:
|
40
47
|
logging.error(
|
41
48
|
f"Error invoking action '{_request['function']}': {e}"
|
@@ -37,65 +37,6 @@ async def communicate(
|
|
37
37
|
operative_model=None,
|
38
38
|
**kwargs,
|
39
39
|
):
|
40
|
-
"""
|
41
|
-
A simpler orchestration than `operate()`, typically without tool invocation.
|
42
|
-
|
43
|
-
**Flow**:
|
44
|
-
1. Sends an instruction (or conversation) to the chat model.
|
45
|
-
2. Optionally parses the response into a structured model or fields.
|
46
|
-
3. Returns either the raw string, the parsed model, or a dict of fields.
|
47
|
-
|
48
|
-
Args:
|
49
|
-
instruction (Instruction | dict, optional):
|
50
|
-
The user's main query or data.
|
51
|
-
guidance (JsonValue, optional):
|
52
|
-
Additional instructions or context for the LLM.
|
53
|
-
context (JsonValue, optional):
|
54
|
-
Extra data or context.
|
55
|
-
plain_content (str, optional):
|
56
|
-
Plain text content appended to the instruction.
|
57
|
-
sender (SenderRecipient, optional):
|
58
|
-
Sender ID (defaults to `Branch.user`).
|
59
|
-
recipient (SenderRecipient, optional):
|
60
|
-
Recipient ID (defaults to `self.id`).
|
61
|
-
progression (ID.IDSeq, optional):
|
62
|
-
Custom ordering of messages.
|
63
|
-
request_model (type[BaseModel] | BaseModel | None, optional):
|
64
|
-
Model for validating or structuring the LLM's response.
|
65
|
-
response_format (type[BaseModel], optional):
|
66
|
-
Alias for `request_model`. If both are provided, raises ValueError.
|
67
|
-
request_fields (dict|list[str], optional):
|
68
|
-
If you only need certain fields from the LLM's response.
|
69
|
-
imodel (iModel, optional):
|
70
|
-
Deprecated alias for `chat_model`.
|
71
|
-
chat_model (iModel, optional):
|
72
|
-
An alternative to the default chat model.
|
73
|
-
parse_model (iModel, optional):
|
74
|
-
If parsing is needed, you can override the default parse model.
|
75
|
-
skip_validation (bool, optional):
|
76
|
-
If True, returns the raw response string unvalidated.
|
77
|
-
images (list, optional):
|
78
|
-
Any relevant images.
|
79
|
-
image_detail (Literal["low","high","auto"], optional):
|
80
|
-
Image detail level (if used).
|
81
|
-
num_parse_retries (int, optional):
|
82
|
-
Maximum parsing retries (capped at 5).
|
83
|
-
fuzzy_match_kwargs (dict, optional):
|
84
|
-
Additional settings for fuzzy field matching (if used).
|
85
|
-
clear_messages (bool, optional):
|
86
|
-
Whether to clear stored messages before sending.
|
87
|
-
operative_model (type[BaseModel], optional):
|
88
|
-
Deprecated, alias for `response_format`.
|
89
|
-
**kwargs:
|
90
|
-
Additional arguments for the underlying LLM call.
|
91
|
-
|
92
|
-
Returns:
|
93
|
-
Any:
|
94
|
-
- Raw string (if `skip_validation=True`),
|
95
|
-
- A validated Pydantic model,
|
96
|
-
- A dict of the requested fields,
|
97
|
-
- or `None` if parsing fails and `handle_validation='return_none'`.
|
98
|
-
"""
|
99
40
|
if operative_model:
|
100
41
|
logging.warning(
|
101
42
|
"operative_model is deprecated. Use response_format instead."
|
@@ -30,11 +30,10 @@ async def interpret(
|
|
30
30
|
# Default temperature if none provided
|
31
31
|
kwargs["temperature"] = kwargs.get("temperature", 0.1)
|
32
32
|
|
33
|
-
refined_prompt = await branch.
|
33
|
+
refined_prompt = await branch.chat(
|
34
34
|
instruction=instruction,
|
35
35
|
guidance=guidance,
|
36
36
|
context=context,
|
37
|
-
skip_validation=True,
|
38
37
|
**kwargs,
|
39
38
|
)
|
40
39
|
return str(refined_prompt)
|
@@ -7,9 +7,14 @@ from typing import TYPE_CHECKING, Literal
|
|
7
7
|
|
8
8
|
from pydantic import BaseModel, JsonValue
|
9
9
|
|
10
|
-
from lionagi.operatives.
|
11
|
-
|
12
|
-
|
10
|
+
from lionagi.operatives.types import (
|
11
|
+
FieldModel,
|
12
|
+
Instruct,
|
13
|
+
ModelParams,
|
14
|
+
Operative,
|
15
|
+
Step,
|
16
|
+
ToolRef,
|
17
|
+
)
|
13
18
|
from lionagi.protocols.types import Instruction, Progression, SenderRecipient
|
14
19
|
from lionagi.service.imodel import iModel
|
15
20
|
|
@@ -110,7 +115,7 @@ async def operate(
|
|
110
115
|
|
111
116
|
# If we want to auto-invoke tools, fetch or generate the schemas
|
112
117
|
if invoke_actions and tools:
|
113
|
-
tool_schemas = branch.acts.get_tool_schema(tools=tools)
|
118
|
+
tool_schemas = tool_schemas or branch.acts.get_tool_schema(tools=tools)
|
114
119
|
|
115
120
|
# 2) Send the instruction to the chat model
|
116
121
|
ins, res = await branch.chat(
|
@@ -138,7 +143,7 @@ async def operate(
|
|
138
143
|
if skip_validation:
|
139
144
|
return operative if return_operative else operative.response_str_dict
|
140
145
|
|
141
|
-
# 5) Parse or validate the response into the operative
|
146
|
+
# 5) Parse or validate the response into the operative's model
|
142
147
|
response_model = operative.update_response_model(res.response)
|
143
148
|
if not isinstance(response_model, BaseModel):
|
144
149
|
# If the response isn't directly a model, attempt a parse
|
@@ -35,42 +35,6 @@ async def parse(
|
|
35
35
|
suppress_conversion_errors: bool = False,
|
36
36
|
response_format=None,
|
37
37
|
):
|
38
|
-
"""Attempts to parse text into a structured Pydantic model.
|
39
|
-
|
40
|
-
Uses optional fuzzy matching to handle partial or unclear fields.
|
41
|
-
|
42
|
-
Args:
|
43
|
-
text (str): The raw text to parse.
|
44
|
-
handle_validation (Literal["raise","return_value","return_none"]):
|
45
|
-
What to do if parsing fails. Defaults to "return_value".
|
46
|
-
max_retries (int):
|
47
|
-
How many times to retry parsing if it fails.
|
48
|
-
request_type (type[BaseModel], optional):
|
49
|
-
The Pydantic model to parse into.
|
50
|
-
operative (Operative, optional):
|
51
|
-
If provided, uses its model and max_retries setting.
|
52
|
-
similarity_algo (str):
|
53
|
-
The similarity algorithm for fuzzy field matching.
|
54
|
-
similarity_threshold (float):
|
55
|
-
A threshold for fuzzy matching (0.0 - 1.0).
|
56
|
-
fuzzy_match (bool):
|
57
|
-
If True, tries to match unrecognized keys to known ones.
|
58
|
-
handle_unmatched (Literal["ignore","raise","remove","fill","force"]):
|
59
|
-
How to handle unmatched fields.
|
60
|
-
fill_value (Any):
|
61
|
-
A default value used when fill is needed.
|
62
|
-
fill_mapping (dict[str, Any] | None):
|
63
|
-
A mapping from field -> fill value override.
|
64
|
-
strict (bool):
|
65
|
-
If True, raises errors on ambiguous fields or data types.
|
66
|
-
suppress_conversion_errors (bool):
|
67
|
-
If True, logs or ignores errors during data conversion.
|
68
|
-
|
69
|
-
Returns:
|
70
|
-
BaseModel | Any | None:
|
71
|
-
The parsed model instance, or a dict/string/None depending
|
72
|
-
on the handling mode.
|
73
|
-
"""
|
74
38
|
_should_try = True
|
75
39
|
num_try = 0
|
76
40
|
response_model = text
|
lionagi/operations/plan/plan.py
CHANGED
@@ -6,7 +6,7 @@ from typing import Any, Literal
|
|
6
6
|
|
7
7
|
from pydantic import BaseModel
|
8
8
|
|
9
|
-
from lionagi.operatives.
|
9
|
+
from lionagi.operatives.types import (
|
10
10
|
LIST_INSTRUCT_FIELD_MODEL,
|
11
11
|
Instruct,
|
12
12
|
InstructResponse,
|
@@ -293,7 +293,7 @@ async def plan(
|
|
293
293
|
# ---------------------------------------------------------
|
294
294
|
# Strategy C: SEQUENTIAL_CONCURRENT_CHUNK
|
295
295
|
# - process plan steps in chunks (one chunk after another),
|
296
|
-
# - each chunk
|
296
|
+
# - each chunk's steps run in parallel.
|
297
297
|
# ---------------------------------------------------------
|
298
298
|
case "sequential_concurrent_chunk":
|
299
299
|
chunk_size = (execution_kwargs or {}).get("chunk_size", 5)
|
@@ -334,7 +334,7 @@ async def plan(
|
|
334
334
|
# Strategy D: CONCURRENT_SEQUENTIAL_CHUNK
|
335
335
|
# - split plan steps into chunks,
|
336
336
|
# - run all chunks in parallel,
|
337
|
-
# - but each chunk
|
337
|
+
# - but each chunk's steps run sequentially.
|
338
338
|
# ---------------------------------------------------------
|
339
339
|
case "concurrent_sequential_chunk":
|
340
340
|
chunk_size = (execution_kwargs or {}).get("chunk_size", 5)
|
@@ -2,6 +2,12 @@
|
|
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
|
@@ -17,9 +23,22 @@ __all__ = ("ActionManager",)
|
|
17
23
|
|
18
24
|
|
19
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
|
+
"""
|
20
31
|
|
21
32
|
def __init__(self, *args: FuncTool, **kwargs) -> None:
|
33
|
+
"""
|
34
|
+
Create an ActionManager, optionally registering initial tools.
|
22
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
|
+
"""
|
23
42
|
super().__init__()
|
24
43
|
self.registry: dict[str, Tool] = {}
|
25
44
|
|
@@ -27,24 +46,19 @@ class ActionManager(Manager):
|
|
27
46
|
if args:
|
28
47
|
tools.extend(to_list(args, dropna=True, flatten=True))
|
29
48
|
if kwargs:
|
30
|
-
tools.extend(
|
31
|
-
|
32
|
-
)
|
49
|
+
tools.extend(to_list(kwargs.values(), dropna=True, flatten=True))
|
50
|
+
|
33
51
|
self.register_tools(tools, update=True)
|
34
52
|
|
35
53
|
def __contains__(self, tool: FuncToolRef) -> bool:
|
36
|
-
"""
|
37
|
-
|
38
|
-
|
39
|
-
-
|
40
|
-
-
|
41
|
-
- Callable function
|
42
|
-
|
43
|
-
Args:
|
44
|
-
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__.
|
45
59
|
|
46
60
|
Returns:
|
47
|
-
bool: True if
|
61
|
+
bool: True if found, else False.
|
48
62
|
"""
|
49
63
|
if isinstance(tool, Tool):
|
50
64
|
return tool.function in self.registry
|
@@ -54,24 +68,21 @@ class ActionManager(Manager):
|
|
54
68
|
return tool.__name__ in self.registry
|
55
69
|
return False
|
56
70
|
|
57
|
-
def register_tool(
|
58
|
-
|
59
|
-
tool
|
60
|
-
update: bool = False,
|
61
|
-
) -> None:
|
62
|
-
"""Register a single tool in the registry.
|
63
|
-
|
64
|
-
If the tool is a callable function, it is automatically converted
|
65
|
-
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.
|
66
74
|
|
67
75
|
Args:
|
68
|
-
tool
|
69
|
-
|
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.
|
70
80
|
|
71
81
|
Raises:
|
72
82
|
ValueError: If tool already registered and update=False.
|
73
|
-
TypeError: If tool is not a Tool
|
83
|
+
TypeError: If `tool` is not a Tool or callable.
|
74
84
|
"""
|
85
|
+
# Check if tool already exists
|
75
86
|
if not update and tool in self:
|
76
87
|
name = None
|
77
88
|
if isinstance(tool, Tool):
|
@@ -80,81 +91,85 @@ class ActionManager(Manager):
|
|
80
91
|
name = tool.__name__
|
81
92
|
raise ValueError(f"Tool {name} is already registered.")
|
82
93
|
|
94
|
+
# Convert raw callable to a Tool if needed
|
83
95
|
if callable(tool):
|
84
96
|
tool = Tool(func_callable=tool)
|
85
97
|
if not isinstance(tool, Tool):
|
86
|
-
raise TypeError(
|
87
|
-
|
98
|
+
raise TypeError(
|
99
|
+
"Must provide a `Tool` object or a callable function."
|
100
|
+
)
|
88
101
|
self.registry[tool.function] = tool
|
89
102
|
|
90
103
|
def register_tools(
|
91
|
-
self,
|
92
|
-
tools: list[FuncTool] | FuncTool,
|
93
|
-
update: bool = False,
|
104
|
+
self, tools: list[FuncTool] | FuncTool, update: bool = False
|
94
105
|
) -> None:
|
95
|
-
"""
|
96
|
-
|
97
|
-
Handles both single tools and lists of tools. Each tool can be
|
98
|
-
either a Tool object or a callable function.
|
106
|
+
"""
|
107
|
+
Register multiple tools at once.
|
99
108
|
|
100
109
|
Args:
|
101
|
-
tools
|
102
|
-
|
110
|
+
tools (list[FuncTool] | FuncTool):
|
111
|
+
A single or list of tools/callables.
|
112
|
+
update (bool):
|
113
|
+
If True, allow updating existing tools.
|
103
114
|
|
104
115
|
Raises:
|
105
|
-
ValueError: If
|
106
|
-
TypeError: If any
|
116
|
+
ValueError: If a duplicate tool is found and update=False.
|
117
|
+
TypeError: If any item is not a Tool or callable.
|
107
118
|
"""
|
108
119
|
tools_list = tools if isinstance(tools, list) else [tools]
|
109
|
-
|
110
|
-
self.register_tool(
|
111
|
-
for tool in to_list(tools_list, dropna=True, flatten=True)
|
112
|
-
]
|
120
|
+
for t in tools_list:
|
121
|
+
self.register_tool(t, update=update)
|
113
122
|
|
114
123
|
def match_tool(
|
115
124
|
self, action_request: ActionRequest | ActionRequestModel | dict
|
116
125
|
) -> FunctionCalling:
|
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
|
+
"""
|
117
137
|
if not isinstance(
|
118
138
|
action_request, ActionRequest | ActionRequestModel | dict
|
119
139
|
):
|
120
140
|
raise TypeError(f"Unsupported type {type(action_request)}")
|
121
141
|
|
122
|
-
func =
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
action_request
|
129
|
-
if isinstance(action_request, dict)
|
130
|
-
else action_request.arguments
|
131
|
-
)
|
132
|
-
tool = self.registry.get(func, 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
|
133
149
|
|
150
|
+
tool = self.registry.get(func, None)
|
134
151
|
if not isinstance(tool, Tool):
|
135
152
|
raise ValueError(f"Function {func} is not registered.")
|
153
|
+
|
136
154
|
return FunctionCalling(func_tool=tool, arguments=args)
|
137
155
|
|
138
156
|
async def invoke(
|
139
|
-
self,
|
140
|
-
|
141
|
-
|
157
|
+
self,
|
158
|
+
func_call: ActionRequestModel | ActionRequest,
|
159
|
+
) -> FunctionCalling:
|
160
|
+
"""
|
161
|
+
High-level API to parse and run a function call.
|
142
162
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
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`.
|
148
167
|
|
149
168
|
Args:
|
150
|
-
func_call:
|
151
|
-
log_manager: Optional logger for execution tracking.
|
169
|
+
func_call: The action request model or ActionRequest object.
|
152
170
|
|
153
171
|
Returns:
|
154
|
-
|
155
|
-
|
156
|
-
Raises:
|
157
|
-
ValueError: If function not registered or call format invalid.
|
172
|
+
`FunctionCalling` event after it completes execution.
|
158
173
|
"""
|
159
174
|
function_calling = self.match_tool(func_call)
|
160
175
|
await function_calling.invoke()
|
@@ -162,11 +177,7 @@ class ActionManager(Manager):
|
|
162
177
|
|
163
178
|
@property
|
164
179
|
def schema_list(self) -> list[dict[str, Any]]:
|
165
|
-
"""
|
166
|
-
|
167
|
-
Returns:
|
168
|
-
List of OpenAI function schemas for all registered tools.
|
169
|
-
"""
|
180
|
+
"""Return the list of JSON schemas for all registered tools."""
|
170
181
|
return [tool.tool_schema for tool in self.registry.values()]
|
171
182
|
|
172
183
|
def get_tool_schema(
|
@@ -175,20 +186,25 @@ class ActionManager(Manager):
|
|
175
186
|
auto_register: bool = True,
|
176
187
|
update: bool = False,
|
177
188
|
) -> dict:
|
178
|
-
"""
|
189
|
+
"""
|
190
|
+
Retrieve schemas for a subset of tools or for all.
|
179
191
|
|
180
192
|
Args:
|
181
|
-
tools:
|
182
|
-
If True, return all tools.
|
183
|
-
|
184
|
-
|
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.
|
185
201
|
|
186
202
|
Returns:
|
187
|
-
|
203
|
+
dict: e.g., {"tools": [list of schemas]}
|
188
204
|
|
189
205
|
Raises:
|
190
|
-
ValueError: If
|
191
|
-
TypeError: If
|
206
|
+
ValueError: If requested tool is not found and auto_register=False.
|
207
|
+
TypeError: If tool specification is invalid.
|
192
208
|
"""
|
193
209
|
if isinstance(tools, list | tuple) and len(tools) == 1:
|
194
210
|
tools = tools[0]
|
@@ -207,10 +223,15 @@ class ActionManager(Manager):
|
|
207
223
|
tool: Any,
|
208
224
|
auto_register: bool = True,
|
209
225
|
update: bool = False,
|
210
|
-
) -> 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
|
+
"""
|
211
231
|
if isinstance(tool, dict):
|
212
|
-
return tool
|
232
|
+
return tool # Already a schema
|
213
233
|
if callable(tool):
|
234
|
+
# Possibly unregistered function
|
214
235
|
name = tool.__name__
|
215
236
|
if name not in self.registry:
|
216
237
|
if auto_register:
|
@@ -233,3 +254,5 @@ class ActionManager(Manager):
|
|
233
254
|
|
234
255
|
|
235
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
|