Skip to content

Commit a880c12

Browse files
committed
--tools is now --functions, can be path, can be multiple
Closes #1016
1 parent 1efb14f commit a880c12

File tree

4 files changed

+39
-18
lines changed

4 files changed

+39
-18
lines changed

docs/help.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ Options:
127127
Attachment with explicit mimetype,
128128
--at image.jpg image/jpeg
129129
-T, --tool TEXT Name of a tool to make available to the model
130-
--tools TEXT Python code block defining functions to
131-
register as tools
130+
--functions TEXT Python code block or file path defining
131+
functions to register as tools
132132
--td, --tools-debug Show full details of tool executions
133133
--ta, --tools-approve Manually approve every tool execution
134134
-o, --option <TEXT TEXT>... key/value options for the model
@@ -628,9 +628,10 @@ Usage: llm tools list [OPTIONS]
628628
List available tools that have been provided by plugins
629629
630630
Options:
631-
--json Output as JSON
632-
--tools TEXT Python code block defining functions to register as tools
633-
--help Show this message and exit.
631+
--json Output as JSON
632+
--functions TEXT Python code block or file path defining functions to
633+
register as tools
634+
--help Show this message and exit.
634635
```
635636

636637
(help-aliases)=

docs/tools.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ In LLM every tool is a defined as a Python function. The function can take any n
2020

2121
Tool functions should include a docstring that describes what the function does. This docstring will become the description that is passed to the model.
2222

23-
The Python API can accept functions directly. The command-line interface has two ways for tools to be defined: via plugins that implement the {ref}`register_tools() plugin hook <plugin-hooks-register-tools>`, or directly on the command-line using the `--tools` argument to specify a block of Python code defining one or more functions.
23+
The Python API can accept functions directly. The command-line interface has two ways for tools to be defined: via plugins that implement the {ref}`register_tools() plugin hook <plugin-hooks-register-tools>`, or directly on the command-line using the `--functions` argument to specify a block of Python code defining one or more functions - or a path to a Python file containing the same.

llm/cli.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,9 @@ def cli():
344344
)
345345
@click.option(
346346
"python_tools",
347-
"--tools",
348-
help="Python code block defining functions to register as tools",
347+
"--functions",
348+
help="Python code block or file path defining functions to register as tools",
349+
multiple=True,
349350
)
350351
@click.option(
351352
"tools_debug",
@@ -757,7 +758,8 @@ def read_prompt():
757758

758759
extra_tools = []
759760
if python_tools:
760-
extra_tools = _tools_from_code(python_tools)
761+
for code_or_path in python_tools:
762+
extra_tools = _tools_from_code(code_or_path)
761763

762764
if tools or python_tools:
763765
prompt_method = conversation.chain
@@ -2286,15 +2288,17 @@ def tools():
22862288
@click.option("json_", "--json", is_flag=True, help="Output as JSON")
22872289
@click.option(
22882290
"python_tools",
2289-
"--tools",
2290-
help="Python code block defining functions to register as tools",
2291+
"--functions",
2292+
help="Python code block or file path defining functions to register as tools",
2293+
multiple=True,
22912294
)
22922295
def tools_list(json_, python_tools):
22932296
"List available tools that have been provided by plugins"
22942297
tools = get_tools()
22952298
if python_tools:
2296-
for tool in _tools_from_code(python_tools):
2297-
tools[tool.name] = tool
2299+
for code_or_path in python_tools:
2300+
for tool in _tools_from_code(code_or_path):
2301+
tools[tool.name] = tool
22982302
if json_:
22992303
click.echo(
23002304
json.dumps(
@@ -3565,16 +3569,21 @@ def load_template(name: str) -> Template:
35653569
return _parse_yaml_template(name, content)
35663570

35673571

3568-
def _tools_from_code(code: str) -> List[Tool]:
3572+
def _tools_from_code(code_or_path: str) -> List[Tool]:
35693573
"""
35703574
Treat all Python functions in the code as tools
35713575
"""
3576+
if "\n" not in code_or_path and code_or_path.endswith(".py"):
3577+
try:
3578+
code_or_path = pathlib.Path(code_or_path).read_text()
3579+
except FileNotFoundError:
3580+
raise click.ClickException("File not found: {}".format(code_or_path))
35723581
globals = {}
35733582
tools = []
35743583
try:
3575-
exec(code, globals)
3584+
exec(code_or_path, globals)
35763585
except SyntaxError as ex:
3577-
raise click.ClickException("Error in --tools definition: {}".format(ex))
3586+
raise click.ClickException("Error in --functions definition: {}".format(ex))
35783587
# Register all callables in the locals dict:
35793588
for name, value in globals.items():
35803589
if callable(value) and not name.startswith("_"):

tests/test_plugins.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ def register_fragment_loaders(self, register):
191191
]
192192

193193

194-
def test_register_tools():
194+
def test_register_tools(tmpdir):
195195
def upper(text: str) -> str:
196196
"""Convert text to uppercase."""
197197
return text.upper()
@@ -271,11 +271,22 @@ def register_tools(self, register):
271271
},
272272
}
273273
# And test the --tools option
274+
functions_path = str(tmpdir / "functions.py")
275+
with open(functions_path, "w") as fp:
276+
fp.write("def example(s: str, i: int):\n return s + '-' + str(i)")
274277
result3 = runner.invoke(
275-
cli.cli, ["tools", "--tools", "def reverse(s: str): return s[::-1]"]
278+
cli.cli,
279+
[
280+
"tools",
281+
"--functions",
282+
"def reverse(s: str): return s[::-1]",
283+
"--functions",
284+
functions_path,
285+
],
276286
)
277287
assert result3.exit_code == 0
278288
assert "reverse(s: str)" in result3.output
289+
assert "example(s: str, i: int)" in result3.output
279290
finally:
280291
plugins.pm.unregister(name="ToolsPlugin")
281292
assert llm.get_tools() == {}

0 commit comments

Comments
 (0)