tool-call
: move usage examples to examples/agent
This commit is contained in:
parent
6610ecf965
commit
0abfa36ca7
7 changed files with 113 additions and 61 deletions
33
examples/agent/README.md
Normal file
33
examples/agent/README.md
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# Agents / Tool Calling w/ llama.cpp
|
||||||
|
|
||||||
|
- Install prerequisite: [uv](https://docs.astral.sh/uv/) (used to simplify python deps)
|
||||||
|
|
||||||
|
- Run `llama-server` w/ jinja templates:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make -j LLAMA_CURL=1 llama-server
|
||||||
|
./llama-server \
|
||||||
|
-mu https://huggingface.co/lmstudio-community/Meta-Llama-3.1-70B-Instruct-GGUF/resolve/main/Meta-Llama-3.1-70B-Instruct-Q4_K_M.gguf \
|
||||||
|
--jinja \
|
||||||
|
-c 8192 -fa
|
||||||
|
```
|
||||||
|
|
||||||
|
- Run some tools inside a docker container (check http://localhost:8088/docs once running):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -p 8088:8088 -w /src \
|
||||||
|
-v $PWD/examples/agent:/src \
|
||||||
|
--rm -it ghcr.io/astral-sh/uv:python3.12-alpine \
|
||||||
|
uv run fastify.py --port 8088 tools.py
|
||||||
|
```
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> The command above gives tools (and your agent) access to the web (and read-only access to `examples/agent/**`. If you're concerned about unleashing a rogue agent on the web, please explore setting up proxies for your docker (and contribute back!)
|
||||||
|
|
||||||
|
- Run the agent with a given goal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv run examples/agent/run.py \
|
||||||
|
--tool-endpoint http://localhost:8088 \
|
||||||
|
--goal "What is the sum of 2535 squared and 32222000403?"
|
||||||
|
```
|
|
@ -22,6 +22,9 @@ import urllib.parse
|
||||||
|
|
||||||
class OpenAPIMethod:
|
class OpenAPIMethod:
|
||||||
def __init__(self, url, name, descriptor, catalog):
|
def __init__(self, url, name, descriptor, catalog):
|
||||||
|
'''
|
||||||
|
Wraps a remote OpenAPI method as a Python function.
|
||||||
|
'''
|
||||||
self.url = url
|
self.url = url
|
||||||
self.__name__ = name
|
self.__name__ = name
|
||||||
|
|
||||||
|
@ -96,11 +99,8 @@ def main(
|
||||||
goal: Annotated[str, typer.Option()],
|
goal: Annotated[str, typer.Option()],
|
||||||
api_key: Optional[str] = None,
|
api_key: Optional[str] = None,
|
||||||
tool_endpoint: Optional[list[str]] = None,
|
tool_endpoint: Optional[list[str]] = None,
|
||||||
format: Annotated[Optional[str], typer.Option(help="The output format: either a Python type (e.g. 'float' or a Pydantic model defined in one of the tool files), or a JSON schema, e.g. '{\"format\": \"date\"}'")] = None,
|
|
||||||
max_iterations: Optional[int] = 10,
|
max_iterations: Optional[int] = 10,
|
||||||
parallel_calls: Optional[bool] = False,
|
|
||||||
verbose: bool = False,
|
verbose: bool = False,
|
||||||
# endpoint: Optional[str] = None,
|
|
||||||
endpoint: str = "http://localhost:8080/v1/",
|
endpoint: str = "http://localhost:8080/v1/",
|
||||||
):
|
):
|
||||||
|
|
||||||
|
@ -110,6 +110,7 @@ def main(
|
||||||
tool_map = {}
|
tool_map = {}
|
||||||
tools = []
|
tools = []
|
||||||
|
|
||||||
|
# Discover tools using OpenAPI catalogs at the provided endpoints.
|
||||||
for url in (tool_endpoint or []):
|
for url in (tool_endpoint or []):
|
||||||
assert url.startswith('http://') or url.startswith('https://'), f'Tools must be URLs, not local files: {url}'
|
assert url.startswith('http://') or url.startswith('https://'), f'Tools must be URLs, not local files: {url}'
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.10"
|
||||||
|
# dependencies = [
|
||||||
|
# "ipython",
|
||||||
|
# ]
|
||||||
|
# ///
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
@ -82,32 +88,69 @@ def _is_serializable(obj) -> bool:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def python(source: str) -> Union[Dict, str]:
|
def python(code: str) -> str:
|
||||||
"""
|
"""
|
||||||
Evaluate a Python program and return the globals it declared.
|
Executes Python code in a siloed environment using IPython and returns the output.
|
||||||
Can be used to compute mathematical expressions (e.g. after importing math module).
|
|
||||||
Args:
|
Parameters:
|
||||||
source: contain valid, executable and pure Python code. Should also import any required Python packages.
|
code (str): The Python code to execute.
|
||||||
For example: "import math\nresult = math.cos(2) * 10"
|
|
||||||
Returns:
|
Returns:
|
||||||
dict | str: A dictionary containing variables declared, or an error message if an exception occurred.
|
str: The output of the executed code.
|
||||||
"""
|
"""
|
||||||
|
from IPython import InteractiveShell
|
||||||
|
from io import StringIO
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Create an isolated IPython shell instance
|
||||||
|
shell = InteractiveShell()
|
||||||
|
|
||||||
|
# Redirect stdout to capture output
|
||||||
|
old_stdout = sys.stdout
|
||||||
|
sys.stdout = mystdout = StringIO()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
namespace = {}
|
# Execute the code
|
||||||
sys.stderr.write(f"Executing Python program:\n{source}\n")
|
shell.run_cell(code)
|
||||||
exec(source, namespace)
|
|
||||||
results = {
|
|
||||||
k: v
|
|
||||||
for k, v in namespace.items()
|
|
||||||
if not k.startswith('_') \
|
|
||||||
and not isinstance(v, type) \
|
|
||||||
and not isinstance(v, types.ModuleType) \
|
|
||||||
and not callable(v) \
|
|
||||||
and _is_serializable(v)
|
|
||||||
}
|
|
||||||
sys.stderr.write(f"Results: {json.dumps(results, indent=2)}\n")
|
|
||||||
return results
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg = f"Error: {sys.exc_info()[1]}"
|
# Restore stdout before returning
|
||||||
sys.stderr.write(f"{msg}\n")
|
sys.stdout = old_stdout
|
||||||
return msg
|
return f"An error occurred: {e}"
|
||||||
|
finally:
|
||||||
|
# Always restore stdout
|
||||||
|
sys.stdout = old_stdout
|
||||||
|
|
||||||
|
# Retrieve the output
|
||||||
|
output = mystdout.getvalue()
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
# def python(source: str) -> Union[Dict, str]:
|
||||||
|
# """
|
||||||
|
# Evaluate a Python program and return the globals it declared.
|
||||||
|
# Can be used to compute mathematical expressions (e.g. after importing math module).
|
||||||
|
# Args:
|
||||||
|
# source: contain valid, executable and pure Python code. Should also import any required Python packages.
|
||||||
|
# For example: "import math\nresult = math.cos(2) * 10"
|
||||||
|
# Returns:
|
||||||
|
# dict | str: A dictionary containing variables declared, or an error message if an exception occurred.
|
||||||
|
# """
|
||||||
|
# try:
|
||||||
|
# namespace = {}
|
||||||
|
# sys.stderr.write(f"Executing Python program:\n{source}\n")
|
||||||
|
# exec(source, namespace)
|
||||||
|
# results = {
|
||||||
|
# k: v
|
||||||
|
# for k, v in namespace.items()
|
||||||
|
# if not k.startswith('_') \
|
||||||
|
# and not isinstance(v, type) \
|
||||||
|
# and not isinstance(v, types.ModuleType) \
|
||||||
|
# and not callable(v) \
|
||||||
|
# and _is_serializable(v)
|
||||||
|
# }
|
||||||
|
# sys.stderr.write(f"Results: {json.dumps(results, indent=2)}\n")
|
||||||
|
# return results
|
||||||
|
# except Exception as e:
|
||||||
|
# msg = f"Error: {sys.exc_info()[1]}"
|
||||||
|
# sys.stderr.write(f"{msg}\n")
|
||||||
|
# return msg
|
|
@ -1,33 +0,0 @@
|
||||||
# Agents / Tool Calling w/ llama.cpp
|
|
||||||
|
|
||||||
- Install prerequisite: [uv](https://docs.astral.sh/uv/) (used to simplify python deps)
|
|
||||||
|
|
||||||
- Run `llama-server` w/ jinja templates:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# make -j LLAMA_CURL=1 llama-server
|
|
||||||
./llama-server \
|
|
||||||
-mu https://huggingface.co/lmstudio-community/Meta-Llama-3.1-70B-Instruct-GGUF/resolve/main/Meta-Llama-3.1-70B-Instruct-Q4_K_M.gguf \
|
|
||||||
--jinja \
|
|
||||||
-c 8192 -fa
|
|
||||||
```
|
|
||||||
|
|
||||||
- Run some tools inside a docker container
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run --rm -it \
|
|
||||||
-p "8088:8088" \
|
|
||||||
-v $PWD/examples/tool-call:/src \
|
|
||||||
ghcr.io/astral-sh/uv:python3.12-alpine \
|
|
||||||
uv run /src/fastify.py --port 8088 /src/tools.py
|
|
||||||
```
|
|
||||||
|
|
||||||
- Verify which tools have been exposed: http://localhost:8088/docs
|
|
||||||
|
|
||||||
- Run the agent with a given goal:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uv run examples/tool-call/agent.py \
|
|
||||||
--tool-endpoint http://localhost:8088 \
|
|
||||||
--goal "What is the sum of 2535 squared and 32222000403 then multiplied by one and a half. What's a third of the result?"
|
|
||||||
```
|
|
|
@ -10,3 +10,5 @@
|
||||||
-r ./requirements/requirements-convert_hf_to_gguf_update.txt
|
-r ./requirements/requirements-convert_hf_to_gguf_update.txt
|
||||||
-r ./requirements/requirements-convert_llama_ggml_to_gguf.txt
|
-r ./requirements/requirements-convert_llama_ggml_to_gguf.txt
|
||||||
-r ./requirements/requirements-convert_lora_to_gguf.txt
|
-r ./requirements/requirements-convert_lora_to_gguf.txt
|
||||||
|
|
||||||
|
-r ./requirements/requirements-agent.txt
|
||||||
|
|
6
requirements/requirements-agent.txt
Normal file
6
requirements/requirements-agent.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
fastapi
|
||||||
|
openai
|
||||||
|
pydantic
|
||||||
|
requests
|
||||||
|
typer
|
||||||
|
uvicorn
|
Loading…
Add table
Add a link
Reference in a new issue