feat: supports functions in bash/js/python/ruby (#6)
This commit is contained in:
@@ -16,10 +16,14 @@ jobs:
|
|||||||
all:
|
all:
|
||||||
name: All
|
name: All
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: extractions/setup-crate@v1
|
- uses: extractions/setup-crate@v1
|
||||||
with:
|
with:
|
||||||
@@ -29,5 +33,18 @@ jobs:
|
|||||||
- name: Check versions
|
- name: Check versions
|
||||||
run: argc version
|
run: argc version
|
||||||
|
|
||||||
- name: Check scripts
|
- name: Setup Node.js
|
||||||
run: argc build
|
uses: actions/setup-node@v4
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- name: Setup Ruby
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
ruby-version: '3.3'
|
||||||
|
|
||||||
|
- name: Run Test
|
||||||
|
run: argc test
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
/tmp
|
/tmp
|
||||||
/functions.txt
|
/functions.txt
|
||||||
|
/functions.txt.test
|
||||||
/functions.json
|
/functions.json
|
||||||
|
/bin
|
||||||
/.env
|
/.env
|
||||||
*.cmd
|
*.cmd
|
||||||
|
__pycache__
|
||||||
+199
-35
@@ -3,60 +3,184 @@ set -e
|
|||||||
|
|
||||||
# @meta dotenv
|
# @meta dotenv
|
||||||
|
|
||||||
|
BIN_DIR=bin
|
||||||
|
|
||||||
|
LANG_CMDS=( \
|
||||||
|
"sh:bash" \
|
||||||
|
"js:node" \
|
||||||
|
"py:python" \
|
||||||
|
"rb:ruby" \
|
||||||
|
)
|
||||||
|
|
||||||
# @cmd Call the function
|
# @cmd Call the function
|
||||||
# @arg func![`_choice_func`] The function name
|
# @arg func![`_choice_func`] The function name
|
||||||
# @arg args~[?`_choice_func_args`] The function args
|
# @arg args~[?`_choice_func_args`] The function args
|
||||||
call() {
|
call() {
|
||||||
"./bin/$argc_func" "${argc_args[@]}"
|
basename="${argc_func%.*}"
|
||||||
|
lang="${argc_func##*.}"
|
||||||
|
func_path="./$lang/$basename.$lang"
|
||||||
|
if [[ ! -e "$func_path" ]]; then
|
||||||
|
_die "error: not found $argc_func"
|
||||||
|
fi
|
||||||
|
if [[ "$lang" == "sh" ]]; then
|
||||||
|
"$func_path" "${argc_args[@]}"
|
||||||
|
else
|
||||||
|
"$(_lang_to_cmd "$lang")" "./cmd/cmd.$lang" "$argc_func"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# @cmd Build all artifacts
|
# @cmd Build the project
|
||||||
|
# @option --names-file=functions.txt Path to a file containing function filenames, one per line.
|
||||||
|
# This file specifies which function files to build.
|
||||||
|
# Example:
|
||||||
|
# get_current_weather.sh
|
||||||
|
# may_execute_js_code.js
|
||||||
build() {
|
build() {
|
||||||
if [[ -f functions.txt ]]; then
|
argc build-declarations-json --names-file "${argc_names_file}"
|
||||||
argc build-declarations-json
|
argc build-bin --names-file "${argc_names_file}"
|
||||||
fi
|
|
||||||
if [[ "$OS" = "Windows_NT" ]]; then
|
|
||||||
argc build-win-shims
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# @cmd Build declarations for specific functions
|
# @cmd Build bin dir
|
||||||
# @option --output=functions.json <FILE> Specify a file path to save the function declarations
|
# @option --names-file=functions.txt Path to a file containing function filenames, one per line.
|
||||||
# @option --names-file=functions.txt Specify a file containing function names
|
build-bin() {
|
||||||
# @arg funcs*[`_choice_func`] The function names
|
if [[ ! -f "$argc_names_file" ]]; then
|
||||||
|
_die "no found "$argc_names_file""
|
||||||
|
fi
|
||||||
|
mkdir -p "$BIN_DIR"
|
||||||
|
rm -rf "$BIN_DIR"/*
|
||||||
|
names=($(cat "$argc_names_file"))
|
||||||
|
invalid_names=()
|
||||||
|
for name in "${names[@]}"; do
|
||||||
|
basename="${name%.*}"
|
||||||
|
lang="${name##*.}"
|
||||||
|
func_file="$lang/$name"
|
||||||
|
if [[ -f "$func_file" ]]; then
|
||||||
|
if _is_win; then
|
||||||
|
bin_file="$BIN_DIR/$basename.cmd"
|
||||||
|
if [[ "$lang" == sh ]]; then
|
||||||
|
_build_win_sh > "$bin_file"
|
||||||
|
else
|
||||||
|
_build_win_lang $lang "$(_lang_to_cmd "$lang")" > "$bin_file"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
bin_file="$BIN_DIR/$basename"
|
||||||
|
if [[ "$lang" == sh ]]; then
|
||||||
|
ln -s "$PWD/$func_file" "$bin_file"
|
||||||
|
else
|
||||||
|
ln -s "$PWD/cmd/cmd.$lang" "$bin_file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
invalid_names+=("$name")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ -n "$invalid_names" ]]; then
|
||||||
|
_die "error: missing following functions: ${invalid_names[*]}"
|
||||||
|
fi
|
||||||
|
echo "Build bin"
|
||||||
|
}
|
||||||
|
|
||||||
|
# @cmd Build declarations.json
|
||||||
|
# @option --output=functions.json <FILE> Path to a json file to save function declarations
|
||||||
|
# @option --names-file=functions.txt Path to a file containing function filenames, one per line.
|
||||||
|
# @arg funcs*[`_choice_func`] The function filenames
|
||||||
build-declarations-json() {
|
build-declarations-json() {
|
||||||
|
set +e
|
||||||
if [[ "${#argc_funcs[@]}" -gt 0 ]]; then
|
if [[ "${#argc_funcs[@]}" -gt 0 ]]; then
|
||||||
names=("${argc_funcs[@]}" )
|
names=("${argc_funcs[@]}" )
|
||||||
elif [[ -f "$argc_names_file" ]]; then
|
elif [[ -f "$argc_names_file" ]]; then
|
||||||
names=($(cat "$argc_names_file"))
|
names=($(cat "$argc_names_file"))
|
||||||
fi
|
fi
|
||||||
if [[ -z "$names" ]]; then
|
if [[ -z "$names" ]]; then
|
||||||
_die "error: no specific function"
|
_die "error: no function for building declarations.json"
|
||||||
fi
|
fi
|
||||||
result=()
|
json_list=()
|
||||||
|
not_found_funcs=()
|
||||||
|
build_failed_funcs=()
|
||||||
for name in "${names[@]}"; do
|
for name in "${names[@]}"; do
|
||||||
result+=("$(build-func-declaration "$name")")
|
lang="${name##*.}"
|
||||||
|
func_file="$lang/$name"
|
||||||
|
if [[ ! -f "$func_file" ]]; then
|
||||||
|
not_found_funcs+=("$name")
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
json_data="$("build-single-declaration" "$name")"
|
||||||
|
status=$?
|
||||||
|
if [ $status -eq 0 ]; then
|
||||||
|
json_list+=("$json_data")
|
||||||
|
else
|
||||||
|
build_failed_funcs+=("$name")
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
echo "["$(IFS=,; echo "${result[*]}")"]" | jq '.' > "$argc_output"
|
if [[ -n "$not_found_funcs" ]]; then
|
||||||
|
_die "error: not found functions: ${not_found_funcs[*]}"
|
||||||
|
fi
|
||||||
|
if [[ -n "$build_failed_funcs" ]]; then
|
||||||
|
_die "error: invalid functions: ${build_failed_funcs[*]}"
|
||||||
|
fi
|
||||||
echo "Build $argc_output"
|
echo "Build $argc_output"
|
||||||
|
echo "["$(IFS=,; echo "${json_list[*]}")"]" | jq '.' > "$argc_output"
|
||||||
}
|
}
|
||||||
|
|
||||||
# @cmd Build declaration for a single function
|
|
||||||
|
# @cmd Build single declaration
|
||||||
# @arg func![`_choice_func`] The function name
|
# @arg func![`_choice_func`] The function name
|
||||||
build-func-declaration() {
|
build-single-declaration() {
|
||||||
argc --argc-export bin/$1 | _parse_declaration
|
func="$1"
|
||||||
|
lang="${func##*.}"
|
||||||
|
cmd="$(_lang_to_cmd "$lang")"
|
||||||
|
if [[ "$lang" == sh ]]; then
|
||||||
|
argc --argc-export "$lang/$func" | _parse_argc_declaration
|
||||||
|
else
|
||||||
|
LLM_FUNCTION_DECLARATE=1 "$cmd" "cmd/cmd.$lang" "$func"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# @cmd Build shims for the functions
|
# @cmd List functions that can be put into functions.txt
|
||||||
# Because Windows OS can't run bash scripts directly, we need to make a shim for each function
|
# Examples:
|
||||||
#
|
# argc --list-functions > functions.txt
|
||||||
# @flag --clear Clear the shims
|
# argc --list-functions --write
|
||||||
build-win-shims() {
|
# argc --list-functions search_duckduckgo.sh >> functions.txt
|
||||||
funcs=($(_choice_func))
|
# @flag -w --write Output to functions.txt
|
||||||
for func in "${funcs[@]}"; do
|
# @arg funcs*[`_choice_func`] The function filenames, list all available functions if not provided
|
||||||
echo "Shim bin/${func}.cmd"
|
list-functions() {
|
||||||
_win_shim > "bin/${func}.cmd"
|
if [[ -n "$argc_write" ]]; then
|
||||||
done
|
_choice_func > functions.txt
|
||||||
|
echo "Write functions.txt"
|
||||||
|
else
|
||||||
|
_choice_func
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# @cmd Test the project
|
||||||
|
# @meta require-tools node,python,ruby
|
||||||
|
test() {
|
||||||
|
names_file=functions.txt.test
|
||||||
|
argc list-functions > "$names_file"
|
||||||
|
argc build --names-file "$names_file"
|
||||||
|
argc test-call-functions
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# @cmd Test call functions
|
||||||
|
test-call-functions() {
|
||||||
|
if _is_win; then
|
||||||
|
ext=".cmd"
|
||||||
|
fi
|
||||||
|
"./bin/may_execute_command$ext" --command 'echo "bash works"'
|
||||||
|
argc call may_execute_command.sh --command 'echo "bash works"'
|
||||||
|
|
||||||
|
export LLM_FUNCTION_DATA='{"code":"console.log(\"javascript works\")"}'
|
||||||
|
"./bin/may_execute_js_code$ext"
|
||||||
|
argc call may_execute_js_code.js
|
||||||
|
|
||||||
|
export LLM_FUNCTION_DATA='{"code":"print(\"python works\")"}'
|
||||||
|
"./bin/may_execute_py_code$ext"
|
||||||
|
argc call may_execute_py_code.py
|
||||||
|
|
||||||
|
export LLM_FUNCTION_DATA='{"code":"puts \"ruby works\""}'
|
||||||
|
"./bin/may_execute_rb_code$ext"
|
||||||
|
argc call may_execute_rb_code.rb
|
||||||
}
|
}
|
||||||
|
|
||||||
# @cmd Install this repo to aichat functions_dir
|
# @cmd Install this repo to aichat functions_dir
|
||||||
@@ -80,7 +204,7 @@ version() {
|
|||||||
curl --version | head -n 1
|
curl --version | head -n 1
|
||||||
}
|
}
|
||||||
|
|
||||||
_parse_declaration() {
|
_parse_argc_declaration() {
|
||||||
jq -r '
|
jq -r '
|
||||||
def parse_description(flag_option):
|
def parse_description(flag_option):
|
||||||
if flag_option.describe == "" then
|
if flag_option.describe == "" then
|
||||||
@@ -123,26 +247,66 @@ _parse_declaration() {
|
|||||||
}'
|
}'
|
||||||
}
|
}
|
||||||
|
|
||||||
_win_shim() {
|
_lang_to_cmd() {
|
||||||
|
match_lang="$1"
|
||||||
|
for item in "${LANG_CMDS[@]}"; do
|
||||||
|
lang="${item%:*}"
|
||||||
|
if [[ "$lang" == "$match_lang" ]]; then
|
||||||
|
echo "${item#*:}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_build_win_sh() {
|
||||||
cat <<-'EOF'
|
cat <<-'EOF'
|
||||||
@echo off
|
@echo off
|
||||||
setlocal
|
setlocal
|
||||||
|
|
||||||
set "script_dir=%~dp0"
|
set "bin_dir=%~dp0"
|
||||||
|
for %%i in ("%bin_dir:~0,-1%") do set "script_dir=%%~dpi"
|
||||||
set "script_name=%~n0"
|
set "script_name=%~n0"
|
||||||
|
set "script_name=%script_name%.sh"
|
||||||
for /f "delims=" %%a in ('argc --argc-shell-path') do set "_bash_prog=%%a"
|
for /f "delims=" %%a in ('argc --argc-shell-path') do set "_bash_prog=%%a"
|
||||||
|
|
||||||
"%_bash_prog%" --noprofile --norc "%script_dir%\%script_name%" %*
|
"%_bash_prog%" --noprofile --norc "%script_dir%sh\%script_name%" %*
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_build_win_lang() {
|
||||||
|
lang="$1"
|
||||||
|
cmd="$2"
|
||||||
|
cat <<-EOF
|
||||||
|
@echo off
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
set "bin_dir=%~dp0"
|
||||||
|
for %%i in ("%bin_dir:~0,-1%") do set "script_dir=%%~dpi"
|
||||||
|
set "script_name=%~n0"
|
||||||
|
|
||||||
|
$cmd "%script_dir%cmd\cmd.$lang" "%script_name%.$lang" %*
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
_is_win() {
|
||||||
|
if [[ "$OS" == "Windows_NT" ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
_choice_func() {
|
_choice_func() {
|
||||||
ls -1 bin | grep -v '\.cmd'
|
for item in "${LANG_CMDS[@]}"; do
|
||||||
|
lang="${item%:*}"
|
||||||
|
ls -1 $lang | grep "\.$lang$"
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
_choice_func_args() {
|
_choice_func_args() {
|
||||||
args=( "${argc__positionals[@]}" )
|
args=( "${argc__positionals[@]}" )
|
||||||
argc --argc-compgen generic "bin/${args[0]}" "${args[@]}"
|
if [[ "${args[0]}" == *.sh ]]; then
|
||||||
|
argc --argc-compgen generic "sh/${args[0]}" "${args[@]}"
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
_die() {
|
_die() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# LLM Functions
|
# LLM Functions
|
||||||
|
|
||||||
This project allows you to enhance large language models (LLMs) with custom functions written in Bash. Imagine your LLM being able to execute system commands, access web APIs, or perform other complex tasks – all triggered by simple, natural language prompts.
|
This project allows you to enhance large language models (LLMs) with custom functions written in Bash/Js/Python/Ruby. Imagine your LLM being able to execute system commands, access web APIs, or perform other complex tasks – all triggered by simple, natural language prompts.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
@@ -18,18 +18,11 @@ Make sure you have the following tools installed:
|
|||||||
git clone https://github.com/sigoden/llm-functions
|
git clone https://github.com/sigoden/llm-functions
|
||||||
```
|
```
|
||||||
|
|
||||||
**2. Build function declarations:**
|
**2. Build function declarations file and bin dir:**
|
||||||
|
|
||||||
Before using the functions, you need to generate a `./functions.json` file that describes the available functions for the LLM.
|
First, create a `./functions.txt` file with each function name on a new line.
|
||||||
|
|
||||||
```sh
|
|
||||||
argc build-declarations <function_names>...
|
|
||||||
```
|
|
||||||
|
|
||||||
Replace `<function_names>...` with the actual names of your functions. Go to the [./bin](https://github.com/sigoden/llm-functions/tree/main/bin) directory for valid function names.
|
|
||||||
|
|
||||||
> 💡 You can also create a `./functions.txt` file with each function name on a new line, Once done, simply run `argc build-declarations` without specifying the function names to automatically use the ones listed in.
|
|
||||||
|
|
||||||
|
Then, run `argc build` to build function declarations file `./functions.json` and bin dir `./bin/`.
|
||||||
|
|
||||||
**3. Configure your AIChat:**
|
**3. Configure your AIChat:**
|
||||||
|
|
||||||
@@ -55,36 +48,6 @@ Now you can interact with your LLM using natural language prompts that trigger y
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Writing Your Own Functions
|
|
||||||
|
|
||||||
Create a new Bash script in the `./bin` directory with the name of your function (e.g., `get_current_weather`) Follow the structure demonstrated in existing examples. For instance:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# @describe Get the current weather in a given location.
|
|
||||||
# @option --location! The city and state, e.g. San Francisco, CA
|
|
||||||
|
|
||||||
main() {
|
|
||||||
curl "https://wttr.in/$(echo "$argc_location" | sed 's/ /+/g')?format=4&M"
|
|
||||||
}
|
|
||||||
|
|
||||||
eval "$(argc --argc-eval "$0" "$@")"
|
|
||||||
```
|
|
||||||
|
|
||||||
The relationship between flags/options and parameters in function declarations is as follows:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# @flag --boolean Parameter `{"type": "boolean"}`
|
|
||||||
# @option --string Parameter `{"type": "string"}`
|
|
||||||
# @option --string-enum[foo|bar] Parameter `{"type": "string", "enum": ["foo", "bar"]}`
|
|
||||||
# @option --integer <INT> Parameter `{"type": "integer"}`
|
|
||||||
# @option --number <NUM> Parameter `{"type": "number"}`
|
|
||||||
# @option --array* <VALUE> Parameter `{"type": "array", "items": {"type":"string"}}`
|
|
||||||
# @option --scalar-required! Use `!` to mark a scalar parameter as required.
|
|
||||||
# @option --array-required+ Use `+` to mark a array parameter as required
|
|
||||||
```
|
|
||||||
|
|
||||||
**After creating your function, don't forget to rebuild the function declarations.**
|
|
||||||
|
|
||||||
## Function Types
|
## Function Types
|
||||||
|
|
||||||
### Retrieve Type
|
### Retrieve Type
|
||||||
@@ -103,6 +66,130 @@ AIChat will ask permission before running the function.
|
|||||||
|
|
||||||
**AIChat categorizes functions starting with `may_` as `execute type` and all others as `retrieve type`.**
|
**AIChat categorizes functions starting with `may_` as `execute type` and all others as `retrieve type`.**
|
||||||
|
|
||||||
|
## Writing Your Own Functions
|
||||||
|
|
||||||
|
The project supports write functions in bash/js/python.
|
||||||
|
|
||||||
|
### Bash
|
||||||
|
|
||||||
|
Create a new bashscript (.e.g. `may_execute_command.sh`) in the [./sh](./sh/) directory.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# @describe Executes a shell command.
|
||||||
|
# @option --command~ Command to execute, such as `ls -la`
|
||||||
|
|
||||||
|
main() {
|
||||||
|
eval $argc_shell_command
|
||||||
|
}
|
||||||
|
|
||||||
|
eval "$(argc --argc-eval "$0" "$@")"
|
||||||
|
```
|
||||||
|
|
||||||
|
`llm-functions` will automatic generate function declaration.json from [comment tags](https://github.com/sigoden/argc?tab=readme-ov-file#comment-tags).
|
||||||
|
|
||||||
|
The relationship between comment tags and parameters in function declarations is as follows:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# @flag --boolean Parameter `{"type": "boolean"}`
|
||||||
|
# @option --string Parameter `{"type": "string"}`
|
||||||
|
# @option --string-enum[foo|bar] Parameter `{"type": "string", "enum": ["foo", "bar"]}`
|
||||||
|
# @option --integer <INT> Parameter `{"type": "integer"}`
|
||||||
|
# @option --number <NUM> Parameter `{"type": "number"}`
|
||||||
|
# @option --array* <VALUE> Parameter `{"type": "array", "items": {"type":"string"}}`
|
||||||
|
# @option --scalar-required! Use `!` to mark a scalar parameter as required.
|
||||||
|
# @option --array-required+ Use `+` to mark a array parameter as required
|
||||||
|
```
|
||||||
|
|
||||||
|
### Javascript
|
||||||
|
|
||||||
|
Create a new javascript (.e.g. `may_execute_command.js`) in the [./js](./js/) directory.
|
||||||
|
|
||||||
|
```js
|
||||||
|
exports.declarate = function declarate() {
|
||||||
|
return {
|
||||||
|
"name": "may_execute_js_code",
|
||||||
|
"description": "Runs the javascript code in node.js.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Javascript code to execute, such as `console.log(\"hello world\")`"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.execute = function execute(data) {
|
||||||
|
eval(data.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python
|
||||||
|
|
||||||
|
Create a new python script in the [./py](./py/) directory (e.g., `may_execute_py_code.py`).
|
||||||
|
|
||||||
|
```py
|
||||||
|
def declarate():
|
||||||
|
return {
|
||||||
|
"name": "may_execute_py_code",
|
||||||
|
"description": "Runs the python code.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "python code to execute, such as `print(\"hello world\")`"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def execute(data):
|
||||||
|
exec(data["code"])
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ruby
|
||||||
|
|
||||||
|
Create a new ruby script in the [./rb](./rb/) directory (e.g., `may_execute_rb_code.rb`).
|
||||||
|
|
||||||
|
```rb
|
||||||
|
def declarate
|
||||||
|
{
|
||||||
|
"name": "may_execute_rb_code",
|
||||||
|
"description": "Runs the ruby code.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Ruby code to execute, such as `puts \"hello world\"`"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute(data)
|
||||||
|
eval(data["code"])
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
The project is under the MIT License, Refer to the [LICENSE](https://github.com/sigoden/llm-functions/blob/main/LICENSE) file for detailed information.
|
The project is under the MIT License, Refer to the [LICENSE](https://github.com/sigoden/llm-functions/blob/main/LICENSE) file for detailed information.
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# @describe Executes a shell command.
|
|
||||||
# @option --shell-command~ "Shell command to execute, such as `ls -la`"
|
|
||||||
|
|
||||||
main() {
|
|
||||||
eval $argc_shell_command
|
|
||||||
}
|
|
||||||
|
|
||||||
eval "$(argc --argc-eval "$0" "$@")"
|
|
||||||
Executable
+36
@@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
function loadModule() {
|
||||||
|
const path = require("path");
|
||||||
|
let func_name = process.argv[1];
|
||||||
|
if (func_name.endsWith("cmd.js")) {
|
||||||
|
func_name = process.argv[2]
|
||||||
|
} else {
|
||||||
|
func_name = path.basename(func_name)
|
||||||
|
}
|
||||||
|
if (!func_name.endsWith(".js")) {
|
||||||
|
func_name += '.js'
|
||||||
|
}
|
||||||
|
const func_path = path.resolve(__dirname, `../js/${func_name}`)
|
||||||
|
try {
|
||||||
|
return require(func_path);
|
||||||
|
} catch {
|
||||||
|
console.log(`Invalid js function: ${func_name}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env["LLM_FUNCTION_DECLARATE"]) {
|
||||||
|
const { declarate } = loadModule();
|
||||||
|
console.log(JSON.stringify(declarate(), null, 2))
|
||||||
|
} else {
|
||||||
|
let data = null;
|
||||||
|
try {
|
||||||
|
data = JSON.parse(process.env["LLM_FUNCTION_DATA"])
|
||||||
|
} catch {
|
||||||
|
console.log("Invalid LLM_FUNCTION_DATA")
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
const { execute } = loadModule();
|
||||||
|
execute(data)
|
||||||
|
}
|
||||||
Executable
+43
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import importlib.util
|
||||||
|
|
||||||
|
def load_module(func_name):
|
||||||
|
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
func_path = os.path.join(base_dir, f"../py/{func_name}")
|
||||||
|
if os.path.exists(func_path):
|
||||||
|
spec = importlib.util.spec_from_file_location(func_name, func_path)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
else:
|
||||||
|
print(f"Invalid py function: {func_name}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
func_name = sys.argv[0]
|
||||||
|
if func_name.endswith("cmd.py"):
|
||||||
|
func_name = sys.argv[1]
|
||||||
|
else:
|
||||||
|
func_name = os.path.basename(func_name)
|
||||||
|
|
||||||
|
if not func_name.endswith(".py"):
|
||||||
|
func_name += ".py"
|
||||||
|
|
||||||
|
if os.getenv("LLM_FUNCTION_DECLARATE"):
|
||||||
|
module = load_module(func_name)
|
||||||
|
declarate = getattr(module, 'declarate')
|
||||||
|
print(json.dumps(declarate(), indent=2))
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
try:
|
||||||
|
data = json.loads(os.getenv("LLM_FUNCTION_DATA"))
|
||||||
|
except (json.JSONDecodeError, TypeError):
|
||||||
|
print("Invalid LLM_FUNCTION_DATA")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
module = load_module(func_name)
|
||||||
|
execute = getattr(module, 'execute')
|
||||||
|
execute(data)
|
||||||
Executable
+36
@@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
|
require 'json'
|
||||||
|
require 'pathname'
|
||||||
|
|
||||||
|
def load_module
|
||||||
|
if __FILE__.end_with?("cmd.rb")
|
||||||
|
func_name = ARGV[0]
|
||||||
|
else
|
||||||
|
func_name = Pathname.new(__FILE__).basename.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
func_name += '.rb' unless func_name.end_with?('.rb')
|
||||||
|
func_path = File.expand_path("../rb/#{func_name}", __dir__)
|
||||||
|
|
||||||
|
begin
|
||||||
|
return require_relative func_path
|
||||||
|
rescue LoadError
|
||||||
|
puts "Invalid ruby function: #{func_name}"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if ENV["LLM_FUNCTION_DECLARATE"]
|
||||||
|
declarate = load_module.method(:declarate)
|
||||||
|
puts JSON.pretty_generate(declarate.call)
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
data = JSON.parse(ENV["LLM_FUNCTION_DATA"])
|
||||||
|
rescue JSON::ParserError
|
||||||
|
puts "Invalid LLM_FUNCTION_DATA"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
execute = load_module.method(:execute)
|
||||||
|
execute.call(data)
|
||||||
|
end
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
exports.declarate = function declarate() {
|
||||||
|
return {
|
||||||
|
"name": "may_execute_js_code",
|
||||||
|
"description": "Runs the javascript code in node.js.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Javascript code to execute, such as `console.log(\"hello world\")`"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.execute = function execute(data) {
|
||||||
|
eval(data.code)
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
def declarate():
|
||||||
|
return {
|
||||||
|
"name": "may_execute_py_code",
|
||||||
|
"description": "Runs the python code.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Python code to execute, such as `print(\"hello world\")`"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def execute(data):
|
||||||
|
exec(data["code"])
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
def declarate
|
||||||
|
{
|
||||||
|
"name": "may_execute_rb_code",
|
||||||
|
"description": "Runs the ruby code.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Ruby code to execute, such as `puts \"hello world\"`"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute(data)
|
||||||
|
eval(data["code"])
|
||||||
|
end
|
||||||
Executable
+11
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# @describe Executes a shell command.
|
||||||
|
# @option --command~ Command to execute, such as `ls -la`
|
||||||
|
|
||||||
|
main() {
|
||||||
|
eval "$argc_command"
|
||||||
|
}
|
||||||
|
|
||||||
|
eval "$(argc --argc-eval "$0" "$@")"
|
||||||
Reference in New Issue
Block a user