Skip to content

Commit 281bf56

Browse files
committed
init commit
0 parents  commit 281bf56

File tree

7 files changed

+237
-0
lines changed

7 files changed

+237
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/tmp
2+
/functions.txt
3+
/functions.json
4+
/.env

Argcfile.sh

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
# @meta dotenv
5+
6+
7+
# @cmd Call the function
8+
# @arg func![`_choice_func`] The function name
9+
# @arg args~[?`_choice_func_args`] The function args
10+
call() {
11+
"./bin/$argc_func" "${argc_args[@]}"
12+
}
13+
14+
# @cmd Build declarations for specific functions
15+
# @option --output=functions.json <FILE> Specify a file path to save the function declarations
16+
# @option --names-file=functions.txt Specify a file containing function names
17+
# @arg funcs*[`_choice_func`] The function names
18+
build-declarations() {
19+
if [[ "${#argc_funcs[@]}" -gt 0 ]]; then
20+
names=("${argc_funcs[@]}" )
21+
elif [[ -f "$argc_names_file" ]]; then
22+
names=($(cat "$argc_names_file"))
23+
fi
24+
if [[ -z "$names" ]]; then
25+
_die "error: no specific function"
26+
fi
27+
result=()
28+
for name in "${names[@]}"; do
29+
result+=("$(build-func-declaration "$name")")
30+
done
31+
echo "["$(IFS=,; echo "${result[*]}")"]" | jq '.' > "$argc_output"
32+
}
33+
34+
# @cmd
35+
# @arg func![`_choice_func`] The function name
36+
build-func-declaration() {
37+
argc --argc-export bin/$1 | \
38+
jq -r '
39+
def parse_description(flag_option):
40+
if flag_option.describe == "" then
41+
{}
42+
else
43+
{ "description": flag_option.describe }
44+
end;
45+
46+
def parse_enum(flag_option):
47+
if flag_option.choice.type == "Values" then
48+
{ "enum": flag_option.choice.data }
49+
else
50+
{}
51+
end;
52+
53+
def parse_property(flag_option):
54+
[
55+
{ condition: (flag_option.flag == true), result: { type: "boolean" } },
56+
{ condition: (flag_option.multiple_occurs == true), result: { type: "array", items: { type: "string" } } },
57+
{ condition: (flag_option.notations[0] == "INT"), result: { type: "integer" } },
58+
{ condition: (flag_option.notations[0] == "NUM"), result: { type: "number" } },
59+
{ condition: true, result: { type: "string" } } ]
60+
| map(select(.condition) | .result) | first
61+
| (. + parse_description(flag_option))
62+
| (. + parse_enum(flag_option))
63+
;
64+
65+
66+
def parse_parameter(flag_options):
67+
{
68+
type: "object",
69+
properties: (reduce flag_options[] as $item ({}; . + { ($item.id | sub("-"; "_"; "g")): parse_property($item) })),
70+
required: [flag_options[] | select(.required == true) | .id],
71+
};
72+
73+
{
74+
name: (.name | sub("-"; "_"; "g")),
75+
description: .describe,
76+
parameters: parse_parameter([.flag_options[] | select(.id != "help" and .id != "version")])
77+
}'
78+
}
79+
80+
_choice_func() {
81+
ls -1 bin
82+
}
83+
84+
_choice_func_args() {
85+
args=( "${argc__positionals[@]}" )
86+
argc --argc-compgen generic "bin/${args[0]}" "${args[@]}"
87+
}
88+
89+
_die() {
90+
echo "$*"
91+
exit 1
92+
}
93+
94+
# See more details at https://github.com/sigoden/argc
95+
eval "$(argc --argc-eval "$0" "$@")"

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) sigoden
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# LLM Functions: Extend LLM with functions written in Bash.
2+
3+
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.
4+
5+
## Prerequisites
6+
7+
Make sure you have the following tools installed:
8+
9+
- [argc](https://github.com/sigoden/argc): A bash command-line framewrok and command runner
10+
- [jq](https://github.com/jqlang/jq): A JSON processor
11+
- [curl](https://curl.se): A command-line tool for transferring data with URLs
12+
13+
## Getting Started with AIChat
14+
15+
**1. Clone the repository:**
16+
17+
```sh
18+
git clone https://github.com/sigoden/llm-functions
19+
```
20+
21+
**2. Build function declarations:**
22+
23+
Before using the functions, you need to generate a `./functions.json` file that describes the available functions for the LLM.
24+
25+
```sh
26+
argc build-declarations <function_names>...
27+
```
28+
29+
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.
30+
31+
> 💡 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.
32+
33+
34+
**3. Configure your aichat application:**
35+
36+
Symlink this repo directory to aichat **functions_dir**:
37+
38+
```sh
39+
ln -s "$(pwd)" "$(aichat --info | grep functions_dir | awk '{print $2}')"
40+
```
41+
42+
Then, add the following settings to your AIChat configuration file:
43+
44+
```yaml
45+
function_calling: true
46+
```
47+
48+
AIChat will automatically load `functions.json` and execute functions located in the `./bin` directory based on your prompts.
49+
50+
**4. Start using your functions:**
51+
52+
Now you can interact with your LLM using natural language prompts that trigger your defined functions. For example:
53+
54+
```
55+
$ aichat -r "%function%" What's the weather in London?
56+
Call Function: get_current_weather --location=London
57+
London: ☀️ 🌡️+18°C 🌬️↑4.7m/s
58+
```
59+
60+
## Writing Your Own Functions
61+
62+
Create a new Bash script in the `./bin` directory with the name of your function (e.g., `get-current-weather`). Use the following structure within the script:
63+
64+
```sh
65+
# @describe Get the current weather in a given location.
66+
# @env TOMORROW_API_KEY! The tomorrow.io api key
67+
# @option --location! The city and state, e.g. San Francisco, CA
68+
69+
main() {
70+
curl "https://wttr.in/$(echo "$argc_location" | sed 's/ /+/g')?format=4&M"
71+
}
72+
73+
eval "$(argc --argc-eval "$0" "$@")"
74+
```
75+
76+
After creating your function, don't forget to rebuild the function declarations (step 2) to include it in your LLM's capabilities.
77+
78+
79+
## License
80+
81+
The project is under the MIT License, Refer to the [LICENSE](https://github.com/sigoden/llm-functions/blob/main/LICENSE) file for detailed information.

bin/duckduckgo_search

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
# @describe Use this function to search DuckDuckGo for a query.
5+
# @meta require-tools ddgr
6+
# @option --query! The query to search for.
7+
# @option --max-results=5 The number of returned results.
8+
9+
main() {
10+
ddgr --num $argc_max_results --json "$argc_query" | \
11+
jq -r '.[] | "**[\(.title)](\(.url))**\n\(.abstract)\n"'
12+
}
13+
14+
eval "$(argc --argc-eval "$0" "$@")"

bin/execute_command

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
# @describe Executes a shell command and returns the output.
5+
# @option --shell-command~ "Shell command to execute, such as `ls -la`"
6+
7+
main() {
8+
eval $argc_shell_command
9+
}
10+
11+
eval "$(argc --argc-eval "$0" "$@")"

bin/get_current_weather

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env bash
2+
set -e
3+
4+
# @describe Get the current weather in a given location.
5+
# @option --location! The city and state, e.g. San Francisco, CA
6+
7+
main() {
8+
curl "https://wttr.in/$(echo "$argc_location" | sed 's/ /+/g')?format=4&M"
9+
}
10+
11+
eval "$(argc --argc-eval "$0" "$@")"

0 commit comments

Comments
 (0)