lionagi 0.7.0__py3-none-any.whl → 0.7.2__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|