refactor: add docs and update scripts (#150)

This commit is contained in:
sigoden
2024-12-13 17:18:01 +08:00
committed by GitHub
parent 8ace93beb8
commit d692625662
6 changed files with 332 additions and 15 deletions
+8 -1
View File
@@ -450,6 +450,13 @@ test-demo@agent() {
done
}
# @cmd Clean the project
clean() {
clean@tool
clean@agent
rm -rf "$BIN_DIR/"*
}
# @cmd Clean tools
# @alias tool:clean
clean@tool() {
@@ -514,7 +521,7 @@ create@tool() {
./scripts/create-tool.sh "$@"
}
# @cmd Show pre-requisite tool versions
# @cmd Displays version information for required tools
version() {
uname -a
if command -v aichat &> /dev/null; then
+79
View File
@@ -0,0 +1,79 @@
# Argcfile
The [Argcfile.sh](https://github.com/sigoden/llm-functions/blob/main/Argcfile.sh) is a powerful Bash script designed to streamline the process of managing LLM functions and agents in your AIChat environment.
We encourage running `Argcfile.sh` using `argc`. Because `argc` provides better autocompletion, it can also be used without trouble on Windows.
Argcfile.sh is to argc what Makefile is to make.
https://github.com/user-attachments/assets/1acef548-4735-49c1-8f60-c4e0baf528de
## Usage
```sh
# -------- Help --------
argc -h # Print help information
argc <command> -h # Print help information for <command>
# -------- Build --------
# Build
argc build
# Build all tools
argc build@tool
# Build specific tools
argc build@tool get_current_weather.sh execute_command.sh
# Build all agents
argc build@agent
# Build specific agents
argc build@agent coder todo
# -------- Run --------
# Run tool
argc run@tool get_current_weather.sh '{"location":"London"}'
# Run agent tool
argc run@agent todo add_todo '{"desc":"Watch a movie"}'
# -------- Test --------
# Test all
argc test
# Test tools
argc test@tool
# Test agents
argc test@agent
# -------- Clean --------
# Clean all
argc clean
# Clean tools
argc clean@tool
# Clean agents
argc clean@agent
# -------- Link --------
argc link-web-search web_search_tavily.sh
argc link-code-interpreter execute_py_code.py
# -------- Misc --------
# Install this repo to aichat functions_dir
argc install
# Displays version information for required tools
argc version
```
## MCP Usage
```sh
# Start/restart the mcp bridge server
argc mcp start
# Stop the mcp bridge server
argc mcp stop
# Run the mcp tool
argc mcp run@tool fs_read_file '{"path":"/tmp/file1"}'
# Show the logs
argc mcp logs
```
+230
View File
@@ -0,0 +1,230 @@
# Tool
This document guides you on creating custom tools for the LLM Functions framework in Bash, JavaScript, and Python.
## Definition via Comments
The key to defining the parameters your tool accepts is through specially formatted comments within your tool's source code.
The `Argcfile.sh` uses these comments to automatically generate the function declaration used by the LLM.
### Json Schema
The following JSON schema includes various types of properties. We will use this as an example to see how to write comments in each language so they can be automatically generated.
```json
{
"name": "demo",
"description": "Demonstrate how to create a tool using Javascript and how to use comments.",
"parameters": {
"type": "object",
"properties": {
"string": {
"type": "string",
"description": "Define a required string property"
},
"string_enum": {
"type": "string",
"enum": [
"foo",
"bar"
],
"description": "Define a required string property with enum"
},
"string_optional": {
"type": "string",
"description": "Define a optional string property"
},
"boolean": {
"type": "boolean",
"description": "Define a required boolean property"
},
"integer": {
"type": "integer",
"description": "Define a required integer property"
},
"number": {
"type": "number",
"description": "Define a required number property"
},
"array": {
"type": "array",
"items": {
"type": "string"
},
"description": "Define a required string array property"
},
"array_optional": {
"type": "array",
"items": {
"type": "string"
},
"description": "Define a optional string array property"
}
},
"required": [
"string",
"string_enum",
"boolean",
"integer",
"number",
"array"
]
}
}
```
### Bash
Use `# @describe`, `# @option`, and `# @flag` comments to define your tool's parameters.
* `# @describe <description>`: A brief description of your tool's functionality. This is required.
* `# @option --<option-name>[!<type>][<constraints>] <description>`: Defines an option.
* `--<option-name>`: The name of the option (use kebab-case).
* `!`: Indicates a required option.
* `<type>`: The data type (e.g., `INT`, `NUM`, `<enum>`). If omitted, defaults to `STRING`.
* `<constraints>`: Any constraints (e.g., `[foo|bar]` for an enum).
* `<description>`: A description of the option.
* `# @flag --<flag-name> <description>`: Defines a boolean flag.
* `--<flag-name>`: The name of the flag (use kebab-case).
* `<description>`: A description of the flag.
**Example ([tools/demo_sh.sh](https://github.com/sigoden/llm-functions/blob/main/tools/demo_sh.sh)):**
```bash
#!/usr/bin/env bash
set -e
# @describe Demonstrate how to create a tool using Bash and how to use comment tags.
# @option --string! Define a required string property
# @option --string-enum![foo|bar] Define a required string property with enum
# @option --string-optional Define a optional string property
# @flag --boolean Define a boolean property
# @option --integer! <INT> Define a required integer property
# @option --number! <NUM> Define a required number property
# @option --array+ <VALUE> Define a required string array property
# @option --array-optional* Define a optional string array property
# @env LLM_OUTPUT=/dev/stdout The output path
main() {
# ... your bash code ...
}
eval "$(argc --argc-eval "$0" "$@")"
```
### JavaScript
Use JSDoc-style comments to define your tool's parameters. The `@typedef` block defines the argument object, and each property within that object represents a parameter.
* `/** ... */`: JSDoc comment block containing the description and parameter definitions.
* `@typedef {Object} Args`: Defines the type of the argument object.
* `@property {<type>} <name> <description>`: Defines a property (parameter) of the `Args` object.
* `<type>`: The data type (e.g., `string`, `boolean`, `number`, `string[]`, `{foo|bar}`).
* `<name>`: The name of the parameter.
* `<description>`: A description of the parameter.
* `[]`: Indicates an optional parameter.
**Example ([tools/demo_js.js](https://github.com/sigoden/llm-functions/blob/main/tools/demo_js.js)):**
```javascript
/**
* Demonstrate how to create a tool using Javascript and how to use comments.
* @typedef {Object} Args
* @property {string} string - Define a required string property
* @property {'foo'|'bar'} string_enum - Define a required string property with enum
* @property {string} [string_optional] - Define a optional string property
* @property {boolean} boolean - Define a required boolean property
* @property {Integer} integer - Define a required integer property
* @property {number} number - Define a required number property
* @property {string[]} array - Define a required string array property
* @property {string[]} [array_optional] - Define a optional string array property
* @param {Args} args
*/
exports.run = function (args) {
// ... your JavaScript code ...
}
```
Of course, you can also use ESM `export` expressions to export functions.
```js
export function run() {
// ... your JavaScript code ...
}
```
### Python
Use type hints and docstrings to define your tool's parameters.
* `def run(...)`: Function definition.
* `<type> <parameter_name>: <description>`: Type hints with descriptions in the docstring.
* `<type>`: The data type (e.g., `str`, `bool`, `int`, `float`, `List[str]`, `Literal["foo", "bar"]`).
* `<parameter_name>`: The name of the parameter.
* `<description>`: Description of the parameter.
* `Optional[...]`: Indicates an optional parameter.
**Example ([tools/demo_py.py](https://github.com/sigoden/llm-functions/blob/main/tools/demo_py.py)):**
```python
def run(
string: str,
string_enum: Literal["foo", "bar"],
boolean: bool,
integer: int,
number: float,
array: List[str],
string_optional: Optional[str] = None,
array_optional: Optional[List[str]] = None,
):
"""Demonstrate how to create a tool using Python and how to use comments.
Args:
string: Define a required string property
string_enum: Define a required string property with enum
boolean: Define a required boolean property
integer: Define a required integer property
number: Define a required number property
array: Define a required string array property
string_optional: Define a optional string property
array_optional: Define a optional string array property
"""
# ... your Python code ...
```
## Quickly create tools
`Argcfile.sh` provides a tool to quickly create script tools.
```
$ argc create@tool --help
Create a boilplate tool script
Examples:
./scripts/create-tool.sh _test.py foo bar! baz+ qux*
USAGE: create-tool [OPTIONS] <NAME> [PARAMS]...
ARGS:
<NAME> The script file name.
[PARAMS]... The script parameters
OPTIONS:
--description <TEXT> The tool description
--force Override the exist tool file
-h, --help Print help
-V, --version Print version
```
```sh
argc create@tool foo bar! baz+ qux*
```
The suffixes after property names represent different meanings.
- `!`: The property is required.
- `*`: The property value must be an array.
- `+`: The property is required, and its value must be an array.
- no suffix: The property is optional.
+1 -1
View File
@@ -8,7 +8,7 @@ set -e
#
# @option --description <text> The tool description
# @flag --force Override the exist tool file
# @arg name! The script file name.
# @arg name! The script file name
# @arg params* The script parameters
main() {
+13 -12
View File
@@ -8,7 +8,8 @@ MCP_JSON_PATH="$ROOT_DIR/mcp.json"
FUNCTIONS_JSON_PATH="$ROOT_DIR/functions.json"
MCP_BRIDGE_PORT="${MCP_BRIDGE_PORT:-8808}"
# @cmd Start/Restart mcp bridge server
# @cmd Start/restart the mcp bridge server
# @alias restart
start() {
if [[ ! -f "$MCP_JSON_PATH" ]]; then
_die "error: not found mcp.json"
@@ -29,7 +30,7 @@ start() {
build-bin
}
# @cmd Stop mcp bridge server
# @cmd Stop the mcp bridge server
stop() {
pid="$(get-server-pid)"
if [[ -n "$pid" ]]; then
@@ -42,12 +43,12 @@ stop() {
"$0" recovery-functions -S
}
# @cmd Run the tool
# @cmd Run the mcp tool
# @arg tool![`_choice_tool`] The tool name
# @arg json The json data
run@tool() {
if [[ -z "$argc_json" ]]; then
declaration="$(build-declarations | jq --arg tool "$argc_tool" -r '.[] | select(.name == $tool)')"
declaration="$(generate-declarations | jq --arg tool "$argc_tool" -r '.[] | select(.name == $tool)')"
if [[ -n "$declaration" ]]; then
_ask_json_data "$declaration"
fi
@@ -58,7 +59,7 @@ run@tool() {
bash "$ROOT_DIR/scripts/run-mcp-tool.sh" "$argc_tool" "$argc_json"
}
# @cmd Show logs
# @cmd Show the logs
# @flag -f --follow Follow mode
logs() {
args=""
@@ -72,7 +73,7 @@ logs() {
# @cmd Build tools to bin
build-bin() {
tools=( $(build-declarations | jq -r '.[].name') )
tools=( $(generate-declarations | jq -r '.[].name') )
for tool in "${tools[@]}"; do
if _is_win; then
bin_file="$BIN_DIR/$tool.cmd"
@@ -88,7 +89,7 @@ build-bin() {
# @cmd Merge mcp tools into functions.json
# @flag -S --save Save to functions.json
merge-functions() {
result="$(jq --argjson json1 "$("$0" recovery-functions)" --argjson json2 "$(build-declarations)" -n '($json1 + $json2)')"
result="$(jq --argjson json1 "$("$0" recovery-functions)" --argjson json2 "$(generate-declarations)" -n '($json1 + $json2)')"
if [[ -n "$argc_save" ]]; then
printf "%s" "$result" > "$FUNCTIONS_JSON_PATH"
else
@@ -111,12 +112,12 @@ recovery-functions() {
fi
}
# @cmd Build tools to bin
build-declarations() {
# @cmd Generate function declarations for the mcp tools
generate-declarations() {
curl -sS http://localhost:$MCP_BRIDGE_PORT/tools | jq '.[] |= . + {mcp: true}'
}
# @cmd Wait for mcp bridge server to ready
# @cmd Wait for the mcp bridge server to ready
wait-for-server() {
while true; do
if [[ "$(curl -fsS http://localhost:$MCP_BRIDGE_PORT/health 2>&1)" == "OK" ]]; then
@@ -126,7 +127,7 @@ wait-for-server() {
done
}
# @cmd
# @cmd Get the server pid
get-server-pid() {
curl -fsSL http://localhost:$MCP_BRIDGE_PORT/pid 2>/dev/null || true
}
@@ -173,7 +174,7 @@ _is_win() {
}
_choice_tool() {
build-declarations | jq -r '.[].name'
generate-declarations | jq -r '.[].name'
}
_die() {
+1 -1
View File
@@ -74,7 +74,7 @@ run() {
if [[ -t 1 ]] && [[ "$skip_confirm" -ne 1 ]]; then
read -r -p "Are you sure you want to continue? [Y/n] " ans
if [[ "$ans" == "N" || "$ans" == "n" ]]; then
echo "error: canceld!" 2>&1
echo "error: canceled!" 2>&1
exit 1
fi
fi