mirror of
https://github.com/ggerganov/llama.cpp.git
synced 2025-01-13 12:10:18 +00:00
agent
: drop fastify.py -> simpler serve_tools.py, and expose other tools to python interpreter
This commit is contained in:
parent
6b4a454735
commit
fa8df0c350
@ -42,25 +42,63 @@
|
||||
docker run -p 8088:8088 -w /src -v $PWD/examples/agent:/src \
|
||||
--env BRAVE_SEARCH_API_KEY=$BRAVE_SEARCH_API_KEY \
|
||||
--rm -it ghcr.io/astral-sh/uv:python3.12-alpine \
|
||||
uv run fastify.py --port 8088 tools/
|
||||
uv run serve_tools.py --port 8088
|
||||
```
|
||||
|
||||
> [!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
|
||||
- Run the agent with some goal
|
||||
|
||||
```bash
|
||||
uv run examples/agent/run.py --tools http://localhost:8088 \
|
||||
"What is the sum of 2535 squared and 32222000403?"
|
||||
```
|
||||
|
||||
<details><summary>See output w/ Hermes-3-Llama-3.1-8B</summary>
|
||||
|
||||
```
|
||||
🛠️ Tools: python, fetch_page, brave_search
|
||||
⚙️ python(code="print(2535**2 + 32222000403)")
|
||||
→ 15 chars
|
||||
The sum of 2535 squared and 32222000403 is 32228426628.
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
```bash
|
||||
uv run examples/agent/run.py --tools http://localhost:8088 \
|
||||
"What is the best BBQ join in Laguna Beach?"
|
||||
"What is the best BBQ joint in Laguna Beach?"
|
||||
```
|
||||
|
||||
<details><summary>See output w/ Hermes-3-Llama-3.1-8B</summary>
|
||||
|
||||
```
|
||||
🛠️ Tools: python, fetch_page, brave_search
|
||||
⚙️ brave_search(query="best bbq joint in laguna beach")
|
||||
→ 4283 chars
|
||||
Based on the search results, Beach Pit BBQ seems to be a popular and highly-rated BBQ joint in Laguna Beach. They offer a variety of BBQ options, including ribs, pulled pork, brisket, salads, wings, and more. They have dine-in, take-out, and catering options available.
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
```bash
|
||||
uv run examples/agent/run.py --tools http://localhost:8088 \
|
||||
"Search for, fetch and summarize the homepage of llama.cpp"
|
||||
```
|
||||
|
||||
<details><summary>See output w/ Hermes-3-Llama-3.1-8B</summary>
|
||||
|
||||
```
|
||||
🛠️ Tools: python, fetch_page, brave_search
|
||||
⚙️ brave_search(query="llama.cpp")
|
||||
→ 3330 chars
|
||||
Llama.cpp is an open-source software library written in C++ that performs inference on various Large Language Models (LLMs). Alongside the library, it includes a CLI and web server. It is co-developed alongside the GGML project, a general-purpose tensor library. Llama.cpp is also available with Python bindings, known as llama.cpp-python. It has gained popularity for its ability to run LLMs on local machines, such as Macs with NVIDIA RTX systems. Users can leverage this library to accelerate LLMs and integrate them into various applications. There are numerous resources available, including tutorials and guides, for getting started with Llama.cpp and llama.cpp-python.
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
- To compare the above results w/ OpenAI's tool usage behaviour, just add `--openai` to the agent invocation (other providers can easily be added, just use the `--endpoint`, `--api-key`, and `--model` flags)
|
||||
|
||||
```bash
|
||||
|
@ -1,105 +0,0 @@
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "aiohttp",
|
||||
# "fastapi",
|
||||
# "html2text",
|
||||
# "ipython",
|
||||
# "pyppeteer",
|
||||
# "typer",
|
||||
# "uvicorn",
|
||||
# ]
|
||||
# ///
|
||||
'''
|
||||
Discovers and binds python script functions as a FastAPI server.
|
||||
|
||||
Usage (docker isolation - with network access):
|
||||
|
||||
docker run -p 8088:8088 -w /src -v $PWD/examples/agent:/src \
|
||||
--env BRAVE_SEARCH_API_KEY=$BRAVE_SEARCH_API_KEY \
|
||||
--rm -it ghcr.io/astral-sh/uv:python3.12-alpine \
|
||||
uv run fastify.py --port 8088 tools/
|
||||
|
||||
Usage (non-siloed, DANGEROUS):
|
||||
|
||||
uv run examples/agent/fastify.py --port 8088 examples/agent/tools
|
||||
|
||||
uv run examples/agent/fastify.py --port 8088 examples/agent/tools/python.py
|
||||
'''
|
||||
import fastapi
|
||||
import importlib.util
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import typer
|
||||
from typing import List
|
||||
import uvicorn
|
||||
|
||||
|
||||
def _load_source_as_module(source):
|
||||
i = 0
|
||||
while (module_name := f'mod_{i}') in sys.modules:
|
||||
i += 1
|
||||
|
||||
spec = importlib.util.spec_from_file_location(module_name, source)
|
||||
assert spec, f'Failed to load {source} as module'
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[module_name] = module
|
||||
assert spec.loader, f'{source} spec has no loader'
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def _load_module(f: str):
|
||||
if f.endswith('.py'):
|
||||
sys.path.insert(0, str(Path(f).parent))
|
||||
return _load_source_as_module(f)
|
||||
else:
|
||||
return importlib.import_module(f)
|
||||
|
||||
|
||||
def main(files: List[str], host: str = '0.0.0.0', port: int = 8000, verbose: bool = False):
|
||||
logging.basicConfig(level=logging.DEBUG if verbose else logging.INFO)
|
||||
|
||||
app = fastapi.FastAPI()
|
||||
|
||||
def load_python(f):
|
||||
logging.info(f'Binding functions from {f}')
|
||||
module = _load_module(f)
|
||||
for k in dir(module):
|
||||
if k.startswith('_'):
|
||||
continue
|
||||
if k == k.capitalize():
|
||||
continue
|
||||
v = getattr(module, k)
|
||||
if not callable(v) or isinstance(v, type):
|
||||
continue
|
||||
if not hasattr(v, '__annotations__'):
|
||||
continue
|
||||
|
||||
vt = type(v)
|
||||
if vt.__module__ == 'langchain_core.tools' and vt.__name__.endswith('Tool') and hasattr(v, 'func') and callable(func := getattr(v, 'func')):
|
||||
v = func
|
||||
|
||||
try:
|
||||
app.post('/' + k)(v)
|
||||
logging.info(f'Bound /{k}')
|
||||
except Exception as e:
|
||||
logging.warning(f'Failed to bind /{k}\n\t{e}')
|
||||
|
||||
|
||||
for f in files:
|
||||
if os.path.isdir(f):
|
||||
for root, _, files in os.walk(f):
|
||||
for file in files:
|
||||
if file.endswith('.py'):
|
||||
load_python(os.path.join(root, file))
|
||||
else:
|
||||
load_python(f)
|
||||
|
||||
uvicorn.run(app, host=host, port=port)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
typer.run(main)
|
@ -8,12 +8,12 @@
|
||||
# "uvicorn",
|
||||
# ]
|
||||
# ///
|
||||
import json
|
||||
import aiohttp
|
||||
import asyncio
|
||||
from functools import wraps
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import aiohttp
|
||||
from functools import wraps
|
||||
from pydantic import BaseModel
|
||||
import sys
|
||||
import typer
|
||||
|
78
examples/agent/serve_tools.py
Normal file
78
examples/agent/serve_tools.py
Normal file
@ -0,0 +1,78 @@
|
||||
# /// script
|
||||
# requires-python = ">=3.11"
|
||||
# dependencies = [
|
||||
# "aiohttp",
|
||||
# "fastapi",
|
||||
# "html2text",
|
||||
# "ipython",
|
||||
# "pyppeteer",
|
||||
# "requests",
|
||||
# "typer",
|
||||
# "uvicorn",
|
||||
# ]
|
||||
# ///
|
||||
'''
|
||||
Runs simple tools as a FastAPI server.
|
||||
|
||||
Usage (docker isolation - with network access):
|
||||
|
||||
docker run -p 8088:8088 -w /src -v $PWD/examples/agent:/src \
|
||||
--env BRAVE_SEARCH_API_KEY=$BRAVE_SEARCH_API_KEY \
|
||||
--rm -it ghcr.io/astral-sh/uv:python3.12-alpine \
|
||||
uv run serve_tools.py --port 8088
|
||||
|
||||
Usage (non-siloed, DANGEROUS):
|
||||
|
||||
uv run examples/agent/serve_tools.py --port 8088
|
||||
'''
|
||||
import logging
|
||||
import re
|
||||
from typing import Optional
|
||||
import fastapi
|
||||
import os
|
||||
import sys
|
||||
import typer
|
||||
import uvicorn
|
||||
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
|
||||
from tools.fetch import fetch_page
|
||||
from tools.search import brave_search
|
||||
from tools.python import python, python_tools
|
||||
|
||||
|
||||
ALL_TOOLS = {
|
||||
fn.__name__: fn
|
||||
for fn in [
|
||||
python,
|
||||
fetch_page,
|
||||
brave_search,
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def main(host: str = '0.0.0.0', port: int = 8000, verbose: bool = False, include: Optional[str] = None, exclude: Optional[str] = None):
|
||||
logging.basicConfig(level=logging.DEBUG if verbose else logging.INFO)
|
||||
|
||||
def accept_tool(name):
|
||||
if include and not re.match(include, name):
|
||||
return False
|
||||
if exclude and re.match(exclude, name):
|
||||
return False
|
||||
return True
|
||||
|
||||
app = fastapi.FastAPI()
|
||||
for name, fn in python_tools.items():
|
||||
if accept_tool(name):
|
||||
app.post(f'/{name}')(fn)
|
||||
if name != 'python':
|
||||
python_tools[name] = fn
|
||||
|
||||
for name, fn in ALL_TOOLS.items():
|
||||
app.post(f'/{name}')(fn)
|
||||
|
||||
uvicorn.run(app, host=host, port=port)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
typer.run(main)
|
@ -4,6 +4,9 @@ import logging
|
||||
import sys
|
||||
|
||||
|
||||
python_tools = {}
|
||||
|
||||
|
||||
def python(code: str) -> str:
|
||||
'''
|
||||
Execute Python code in a siloed environment using IPython and returns the output.
|
||||
@ -16,6 +19,7 @@ def python(code: str) -> str:
|
||||
'''
|
||||
logging.debug('[python] Executing %s', code)
|
||||
shell = InteractiveShell()
|
||||
shell.user_global_ns.update(python_tools)
|
||||
|
||||
old_stdout = sys.stdout
|
||||
sys.stdout = out = StringIO()
|
||||
|
Loading…
Reference in New Issue
Block a user