Cleanup examples and README (#8)

As the title states.

Also added in a prompt/execute capabiltiy into the CLI for the ``arcade
run`` command

Committed by @nbarbettini 
Original work by @Spartee
This commit is contained in:
Sam Partee 2024-07-24 09:10:31 -07:00 committed by GitHub
parent 8964111023
commit 90f1146968
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 192 additions and 45 deletions

View file

@ -1,39 +1,76 @@
[![Release](https://img.shields.io/github/v/release/spartee/arcade-ai)](https://img.shields.io/github/v/release/spartee/arcade-ai)
[![Build status](https://img.shields.io/github/actions/workflow/status/spartee/arcade-ai/main.yml?branch=main)](https://github.com/spartee/arcade-ai/actions/workflows/main.yml?query=branch%3Amain)
[![codecov](https://codecov.io/gh/spartee/arcade-ai/branch/main/graph/badge.svg)](https://codecov.io/gh/spartee/arcade-ai)
[![Commit activity](https://img.shields.io/github/commit-activity/m/spartee/arcade-ai)](https://img.shields.io/github/commit-activity/m/spartee/arcade-ai)
[![License](https://img.shields.io/github/license/spartee/arcade-ai)](https://img.shields.io/github/license/spartee/arcade-ai)
[![Release](https://img.shields.io/github/v/release/arcadeai/arcade-ai)](https://img.shields.io/github/v/release/arcadeai/arcade-ai)
[![Build status](https://img.shields.io/github/actions/workflow/status/arcadeai/arcade-ai/main.yml?branch=main)](https://github.com/arcadeai/arcade-ai/actions/workflows/main.yml?query=branch%3Amain)
[![codecov](https://codecov.io/gh/arcadeai/arcade-ai/branch/main/graph/badge.svg)](https://codecov.io/gh/arcadeai/arcade-ai)
[![Commit activity](https://img.shields.io/github/commit-activity/m/arcadeai/arcade-ai)](https://img.shields.io/github/commit-activity/m/arcadeai/arcade-ai)
[![License](https://img.shields.io/github/license/arcadeai/arcade-ai)](https://img.shields.io/github/license/arcadeai/arcade-ai)
# Arcade-AI
ToolServe is a framework specifically designed to manage and orchestrate Language Learning Models (LLMs) or "tools" with high efficiency. It distinctively separates the tools from the orchestration framework to improve dependency management, packaging, and execution.
# Arcade AI
The server enhances data management capabilities, enabling LLMs to interact with structured data efficiently without the need to return the entire dataset to the context window.
Arcade AI is the developer platform for building tools designed to be used with language models. With Arcade, developers can create, deploy, and easily integrate new tools with language models to enhance their capabilities.
This functionality is especially beneficial for agents tasked with performing actions or responding to queries based on extensive structured datasets.
## `arcade-ai`
## Components
### 1. Command Line Interface (CLI)
The CLI component, located at `toolserve/cli/main.py`, offers commands to package, serve, and inspect LLM "tools". It utilizes the Typer library to manage command-line arguments and options.
### 2. Server
The server component, which manages the storage of artifacts, data, and logs generated by the tools, is implemented using FastAPI and can be found at `toolserve/server/main.py`. The server configuration includes routes, middleware, and database connections.
### 3. SDK
Located at `toolserve/sdk`, the SDK streamlines the development of tools by providing decorators and helper functions that abstract routine tasks, allowing developers to concentrate on the logic of the tool rather than on repetitive code.
### 4. Builtins
Built-in tools for common tasks such as SQL queries are available at `toolserve/builtin/default`. These tools are ready to use and require no additional setup.
The `arcade-ai` package contains:
- `arcade` CLI
- `arcade.sdk` Tool SDK
- `arcade.actor` serving tools with FastAPI, Flask, or Django
## Installation
To install the ToolServe package, execute the following command:
To install the Arcade AI package, execute the following command:
```bash
pip install toolserve
pip install arcade-ai
```
or install from source:
```bash
git clone https://github.com/arcadeai/arcade-ai.git
cd arcade-ai
pip install poetry
poetry install
```
## First steps
Follow these steps if you've cloned the repo and installed the package from source:
```bash
cd examples/websearch
poetry install
arcade show arcade_websearch
```
This will show an output that looks like
┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━┓
┃ Name ┃ Description ┃ Toolkit ┃ Version ┃
┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━┩
│ SearchGoogle │ Search Google using SerpAPI and return organic search results. │ websearch │ 0.1.0 │
└──────────────┴────────────────────────────────────────────────────────────────┴───────────┴─────────┘
Predict the parameters with a model and run the tool with the predicted parameters. Arcade adds the `execute` choice to the tool, which allows you to run the tool with the predicted parameters in a single request.
```bash
> arcade run arcade_websearch "who is Sam Partee?" --choice "execute"
Running tool: SearchGoogle with params: {'query': 'Sam Partee'}
[{"position": 1, "title": "Sam Partee (@SamPartee) / X", "link": "https://twitter.com/sampartee", "redirect_link":
"https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://twitter.com/sampartee&ved=2ahUKEwjBwKiz3b6HAxV1VTABHXL8BZQQFnoECAYQAQ",
"displayed_link": "1.5K+ followers", "thumbnail":
.....
.. (truncated)
```
Arcade also adds the `predict` choice to the tool, which allows you to predict the parameters with a model.
```bash
> arcade run arcade_websearch "who is Sam Partee?" --choice "predict" # also the default
Running tool: SearchGoogle with params: {'query': 'Sam Partee'}
Sam Partee is a CTO, Co-founder of Arcade AI and former Machine Learning Engineer at companies like RedisInc and HPE_Cray. They have
expertise in AI/ML, vector search, Python, HPC, and are a sports fan.
```

View file

@ -90,13 +90,13 @@ def show(
@cli.command(help="Run a tool using an LLM to predict the arguments")
def run(
def run( # noqa: C901
toolkit: str = typer.Argument(..., help="The toolkit to add to model calls"),
prompt: str = typer.Argument(..., help="The prompt to use for context"),
model: str = typer.Option("gpt-3.5-turbo", "-m", help="The model to use for prediction."),
tool: str = typer.Option(None, "-t", "--tool", help="The name of the tool to run."),
choice: str = typer.Option(
"required", "-c", "--choice", help="The value of the tool choice argument"
"prompt", "-c", "--choice", help="The value of the tool choice argument"
),
stream: bool = typer.Option(True, "-s", "--stream", help="Stream the tool output."),
actor: Optional[str] = typer.Option(
@ -137,7 +137,9 @@ def run(
# TODO put in the engine url from config
client = EngineClient()
calls = client.call_tool(tools, tool_choice=choice, prompt=prompt, model=model)
# TODO better way of doing this
tool_choice = "required" if choice in ["prompt", "execute"] else choice
calls = client.call_tool(tools, tool_choice=tool_choice, prompt=prompt, model=model)
messages = [
{"role": "user", "content": prompt},
@ -147,6 +149,7 @@ def run(
called_tool = catalog[tool_name]
console.print(f"Running tool: {tool_name} with params: {parameters}", style="bold blue")
# TODO async.gather instead of loop.
output = asyncio.run(
ToolExecutor.run(
called_tool.tool,
@ -160,20 +163,23 @@ def run(
if output.data:
console.print(output.data.result, style="bold red")
else:
# TODO: Add the tool results to the response in a safer way
messages += [
{
"role": "assistant",
# TODO: escape the output and ensure serialization works
"content": f"Results of Tool {tool_name}: {output.data.result!s}", # type: ignore[union-attr]
},
]
if stream:
stream_response = client.stream_complete(model=model, messages=messages)
display_streamed_markdown(stream_response)
else:
response = client.complete(model=model, messages=messages)
console.print(response.choices[0].message.content, style="bold green")
if choice == "prompt":
# TODO: Add the tool results to the response in a safer way
messages += [
{
"role": "assistant",
# TODO: escape the output and ensure serialization works
"content": f"Results of Tool {tool_name}: {output.data.result!s}", # type: ignore[union-attr]
},
]
if stream:
stream_response = client.stream_complete(model=model, messages=messages)
display_streamed_markdown(stream_response)
else:
response = client.complete(model=model, messages=messages)
console.print(response.choices[0].message.content, style="bold green")
elif choice == "execute":
console.print(output.data.result, style="green") # type: ignore[union-attr]
except RuntimeError as e:
error_message = f"❌ Failed to run tool{': '+ escape(str(e)) if str(e) else ''}"

View file

@ -0,0 +1,43 @@
from arcade.actor.fastapi.actor import FastAPIActor
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from openai import AsyncOpenAI
from arcade_example_nate.tools import arithmetic
client = AsyncOpenAI(base_url="http://localhost:6901")
app = FastAPI()
actor = FastAPIActor(app)
actor.register_tool(arithmetic.add)
actor.register_tool(arithmetic.multiply)
actor.register_tool(arithmetic.divide)
actor.register_tool(arithmetic.sqrt)
class ChatRequest(BaseModel):
message: str
@app.post("/chat")
async def chat(request: ChatRequest):
try:
raw_response = await client.chat.completions.with_raw_response.create(
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": request.message},
],
model="gpt-4o-mini",
max_tokens=150,
tool_choice="execute",
)
chat_completion = raw_response.parse()
return {
"response": chat_completion.choices[0].message.content.strip(),
"tool_call_count": raw_response.headers["arcade-tool-calls"],
"tool_call_duration_ms": raw_response.headers["arcade-total-tool-duration"],
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))

View file

@ -0,0 +1,44 @@
import math
from typing import Annotated
from arcade.sdk.tool import tool
@tool
def add(
a: Annotated[int, "The first number"], b: Annotated[int, "The second number"]
) -> Annotated[int, "The sum of the two numbers"]:
"""
Add two numbers together
"""
return a + b
@tool
def multiply(
a: Annotated[int, "The first number"], b: Annotated[int, "The second number"]
) -> Annotated[int, "The product of the two numbers"]:
"""
Multiply two numbers together
"""
return a * b
@tool
def divide(
a: Annotated[int, "The first number"], b: Annotated[int, "The second number"]
) -> Annotated[float, "The quotient of the two numbers"]:
"""
Divide two numbers
"""
return a / b
@tool
def sqrt(
a: Annotated[int, "The number to square root"],
) -> Annotated[float, "The square root of the number"]:
"""
Get the square root of a number
"""
return math.sqrt(a)

View file

@ -0,0 +1,17 @@
[tool.poetry]
name = "arcade_arithmetic"
version = "0.1.0"
description = "Nate's testing package for Arcade"
authors = ["Nate <nate@arcade-ai.com>"]
[tool.poetry.dependencies]
python = "^3.10"
fastapi = "^0.110.3"
[tool.poetry.dev-dependencies]
pytest = "^7.4"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"