feat: js/py generate declarations from comments (#30)
This commit is contained in:
+53
-49
@@ -22,26 +22,32 @@ call() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# @cmd Build the project
|
# @cmd Build the project
|
||||||
# @option --names-file=functions.txt Path to a file containing function filenames, one per line.
|
# @option --names-file=functions.txt Path to a file containing tool filenames, one per line.
|
||||||
|
# @option --declarations-file=functions.json <FILE> Path to a json file to save function declarations
|
||||||
# This file specifies which function files to build.
|
# This file specifies which function files to build.
|
||||||
# Example:
|
# Example:
|
||||||
# get_current_weather.sh
|
# get_current_weather.sh
|
||||||
# may_execute_js_code.js
|
# may_execute_js_code.js
|
||||||
build() {
|
build() {
|
||||||
argc build-declarations-json --names-file "${argc_names_file}"
|
argc build-declarations-json --names-file "${argc_names_file}" --declarations-file "${argc_declarations_file}"
|
||||||
argc build-bin --names-file "${argc_names_file}"
|
argc build-bin --names-file "${argc_names_file}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# @cmd Build bin dir
|
# @cmd Build tool binaries
|
||||||
# @option --names-file=functions.txt Path to a file containing function filenames, one per line.
|
# @option --names-file=functions.txt Path to a file containing tool filenames, one per line.
|
||||||
|
# @arg tools*[`_choice_tool`] The tool filenames
|
||||||
build-bin() {
|
build-bin() {
|
||||||
if [[ ! -f "$argc_names_file" ]]; then
|
if [[ "${#argc_tools[@]}" -gt 0 ]]; then
|
||||||
_die "no found "$argc_names_file""
|
names=("${argc_tools[@]}" )
|
||||||
|
elif [[ -f "$argc_names_file" ]]; then
|
||||||
|
names=($(cat "$argc_names_file"))
|
||||||
|
fi
|
||||||
|
if [[ -z "$names" ]]; then
|
||||||
|
_die "error: no tools selected"
|
||||||
fi
|
fi
|
||||||
mkdir -p "$BIN_DIR"
|
mkdir -p "$BIN_DIR"
|
||||||
rm -rf "$BIN_DIR"/*
|
rm -rf "$BIN_DIR"/*
|
||||||
names=($(cat "$argc_names_file"))
|
not_found_tools=()
|
||||||
not_found_funcs=()
|
|
||||||
for name in "${names[@]}"; do
|
for name in "${names[@]}"; do
|
||||||
basename="${name%.*}"
|
basename="${name%.*}"
|
||||||
lang="${name##*.}"
|
lang="${name##*.}"
|
||||||
@@ -52,14 +58,14 @@ build-bin() {
|
|||||||
_build_win_shim $lang > "$bin_file"
|
_build_win_shim $lang > "$bin_file"
|
||||||
else
|
else
|
||||||
bin_file="$BIN_DIR/$basename"
|
bin_file="$BIN_DIR/$basename"
|
||||||
ln -s "$PWD/scripts/run-tool.$lang" "$bin_file"
|
ln -s -f "$PWD/scripts/run-tool.$lang" "$bin_file"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
not_found_funcs+=("$name")
|
not_found_tools+=("$name")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [[ -n "$not_found_funcs" ]]; then
|
if [[ -n "$not_found_tools" ]]; then
|
||||||
_die "error: not founds functions: ${not_found_funcs[*]}"
|
_die "error: not found tools: ${not_found_tools[*]}"
|
||||||
fi
|
fi
|
||||||
for name in "$BIN_DIR"/*; do
|
for name in "$BIN_DIR"/*; do
|
||||||
echo "Build $name"
|
echo "Build $name"
|
||||||
@@ -67,73 +73,71 @@ build-bin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# @cmd Build declarations.json
|
# @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 tool filenames, one per line.
|
||||||
# @option --names-file=functions.txt Path to a file containing function filenames, one per line.
|
# @option --declarations-file=functions.json <FILE> Path to a json file to save function declarations
|
||||||
# @arg funcs*[`_choice_func`] The function filenames
|
# @arg tools*[`_choice_tool`] The tool filenames
|
||||||
build-declarations-json() {
|
build-declarations-json() {
|
||||||
if [[ "${#argc_funcs[@]}" -gt 0 ]]; then
|
if [[ "${#argc_tools[@]}" -gt 0 ]]; then
|
||||||
names=("${argc_funcs[@]}" )
|
names=("${argc_tools[@]}" )
|
||||||
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 function for building declarations.json"
|
_die "error: no tools selected"
|
||||||
fi
|
fi
|
||||||
json_list=()
|
json_list=()
|
||||||
not_found_funcs=()
|
not_found_tools=()
|
||||||
build_failed_funcs=()
|
build_failed_tools=()
|
||||||
for name in "${names[@]}"; do
|
for name in "${names[@]}"; do
|
||||||
lang="${name##*.}"
|
lang="${name##*.}"
|
||||||
func_file="tools/$name"
|
func_file="tools/$name"
|
||||||
if [[ ! -f "$func_file" ]]; then
|
if [[ ! -f "$func_file" ]]; then
|
||||||
not_found_funcs+=("$name")
|
not_found_tools+=("$name")
|
||||||
continue;
|
continue;
|
||||||
fi
|
fi
|
||||||
json_data="$("build-single-declaration" "$name")" || {
|
json_data="$(build-tool-declaration "$name")" || {
|
||||||
build_failed_funcs+=("$name")
|
build_failed_tools+=("$name")
|
||||||
}
|
}
|
||||||
json_list+=("$json_data")
|
json_list+=("$json_data")
|
||||||
done
|
done
|
||||||
if [[ -n "$not_found_funcs" ]]; then
|
if [[ -n "$not_found_tools" ]]; then
|
||||||
_die "error: not found functions: ${not_found_funcs[*]}"
|
_die "error: not found tools: ${not_found_tools[*]}"
|
||||||
fi
|
fi
|
||||||
if [[ -n "$build_failed_funcs" ]]; then
|
if [[ -n "$build_failed_tools" ]]; then
|
||||||
_die "error: invalid functions: ${build_failed_funcs[*]}"
|
_die "error: invalid tools: ${build_failed_tools[*]}"
|
||||||
fi
|
fi
|
||||||
echo "Build $argc_output"
|
echo "Build $argc_declarations_file"
|
||||||
echo "["$(IFS=,; echo "${json_list[*]}")"]" | jq '.' > "$argc_output"
|
echo "["$(IFS=,; echo "${json_list[*]}")"]" | jq '.' > "$argc_declarations_file"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# @cmd Build single declaration
|
# @cmd Build function declaration for a tool
|
||||||
# @arg func![`_choice_func`] The function name
|
# @arg tool![`_choice_tool`] The function name
|
||||||
build-single-declaration() {
|
build-tool-declaration() {
|
||||||
func="$1"
|
lang="${1##*.}"
|
||||||
lang="${func##*.}"
|
|
||||||
cmd="$(_lang_to_cmd "$lang")"
|
cmd="$(_lang_to_cmd "$lang")"
|
||||||
LLM_FUNCTION_ACTION=declarate "$cmd" "scripts/run-tool.$lang" "$func"
|
"$cmd" "scripts/build-declarations.$lang" "tools/$1" | jq '.[0]'
|
||||||
}
|
}
|
||||||
|
|
||||||
# @cmd List functions that can be put into functions.txt
|
# @cmd List tools that can be put into functions.txt
|
||||||
# Examples:
|
# Examples:
|
||||||
# argc --list-functions > functions.txt
|
# argc list-tools > functions.txt
|
||||||
# argc --list-functions search_duckduckgo.sh >> functions.txt
|
list-tools() {
|
||||||
# @arg funcs*[`_choice_func`] The function filenames, list all available functions if not provided
|
_choice_tool
|
||||||
list-functions() {
|
|
||||||
_choice_func
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# @cmd Test the project
|
# @cmd Test the project
|
||||||
test() {
|
test() {
|
||||||
func_names_file=functions.txt.test
|
mkdir -p tmp/tests
|
||||||
argc list-functions > "$func_names_file"
|
names_file=tmp/tests/functions.txt
|
||||||
argc build --names-file "$func_names_file"
|
declarations_file=tmp/tests/functions.json
|
||||||
argc test-functions
|
argc list-tools > "$names_file"
|
||||||
rm -rf "$func_names_file"
|
argc build --names-file "$names_file" --declarations-file "$declarations_file"
|
||||||
|
argc test-tools
|
||||||
}
|
}
|
||||||
|
|
||||||
# @cmd Test call functions
|
# @cmd Test call functions
|
||||||
test-functions() {
|
test-tools() {
|
||||||
if _is_win; then
|
if _is_win; then
|
||||||
ext=".cmd"
|
ext=".cmd"
|
||||||
fi
|
fi
|
||||||
@@ -172,7 +176,7 @@ install() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# @cmd Create a boilplate tool script file.
|
# @cmd Create a boilplate tool scriptfile.
|
||||||
# @arg args~
|
# @arg args~
|
||||||
create() {
|
create() {
|
||||||
./scripts/create-tool.sh "$@"
|
./scripts/create-tool.sh "$@"
|
||||||
@@ -239,7 +243,7 @@ _is_win() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
_choice_func() {
|
_choice_tool() {
|
||||||
for item in "${LANG_CMDS[@]}"; do
|
for item in "${LANG_CMDS[@]}"; do
|
||||||
lang="${item%:*}"
|
lang="${item%:*}"
|
||||||
cmd="${item#*:}"
|
cmd="${item#*:}"
|
||||||
|
|||||||
@@ -72,7 +72,9 @@ AIChat will ask permission before running the function.
|
|||||||
|
|
||||||
## Writing Your Own Functions
|
## Writing Your Own Functions
|
||||||
|
|
||||||
The project supports write functions in bash/js/python.
|
You can write functions in bash/javascript/python.
|
||||||
|
|
||||||
|
`llm-functions` will automatic generate function declarations from comments. Refer to `demo_tool.{sh,js,py}` for examples of how to use comments for autogeneration of declarations.
|
||||||
|
|
||||||
### Bash
|
### Bash
|
||||||
|
|
||||||
@@ -92,47 +94,19 @@ main() {
|
|||||||
eval "$(argc --argc-eval "$0" "$@")"
|
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
|
### Javascript
|
||||||
|
|
||||||
Create a new javascript in the [./tools/](./tools/) directory (.e.g. `may_execute_js_code.js`).
|
Create a new javascript in the [./tools/](./tools/) directory (.e.g. `may_execute_js_code.js`).
|
||||||
|
|
||||||
```js
|
```js
|
||||||
exports.declarate = function declarate() {
|
/**
|
||||||
return {
|
* Runs the javascript code in node.js.
|
||||||
"name": "may_execute_js_code",
|
* @typedef {Object} Args
|
||||||
"description": "Runs the javascript code in node.js.",
|
* @property {string} code - Javascript code to execute, such as `console.log("hello world")`
|
||||||
"parameters": {
|
* @param {Args} args
|
||||||
"type": "object",
|
*/
|
||||||
"properties": {
|
exports.main = function main({ code }) {
|
||||||
"code": {
|
eval(code);
|
||||||
"type": "string",
|
|
||||||
"description": "Javascript code to execute, such as `console.log(\"hello world\")`"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.execute = function execute(data) {
|
|
||||||
eval(data.code)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -142,27 +116,13 @@ exports.execute = function execute(data) {
|
|||||||
Create a new python script in the [./tools/](./tools/) directory (e.g., `may_execute_py_code.py`).
|
Create a new python script in the [./tools/](./tools/) directory (e.g., `may_execute_py_code.py`).
|
||||||
|
|
||||||
```py
|
```py
|
||||||
def declarate():
|
def main(code: str):
|
||||||
return {
|
"""Runs the python code.
|
||||||
"name": "may_execute_py_code",
|
Args:
|
||||||
"description": "Runs the python code.",
|
code: Python code to execute, such as `print("hello world")`
|
||||||
"parameters": {
|
"""
|
||||||
"type": "object",
|
exec(code)
|
||||||
"properties": {
|
|
||||||
"code": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "python code to execute, such as `print(\"hello world\")`"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def execute(data):
|
|
||||||
exec(data["code"])
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|||||||
@@ -0,0 +1,203 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const fs = require("fs");
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const scriptfile = process.argv[2];
|
||||||
|
const contents = fs.readFileSync(process.argv[2], "utf8");
|
||||||
|
const functions = extractFunctions(contents);
|
||||||
|
let declarations = functions.map(({ funcName, jsdoc }) => {
|
||||||
|
const { description, params } = parseJsDoc(jsdoc, funcName);
|
||||||
|
const declaration = buildDeclaration(funcName, description, params);
|
||||||
|
return declaration;
|
||||||
|
});
|
||||||
|
const name = getBasename(scriptfile);
|
||||||
|
if (declarations.length > 0) {
|
||||||
|
declarations = declarations.slice(0, 1);
|
||||||
|
declarations[0].name = name;
|
||||||
|
}
|
||||||
|
console.log(JSON.stringify(declarations, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} contents
|
||||||
|
* @param {bool} isTool
|
||||||
|
*/
|
||||||
|
function extractFunctions(contents, isTool = true) {
|
||||||
|
const output = [];
|
||||||
|
const lines = contents.split("\n");
|
||||||
|
let isInComment = false;
|
||||||
|
let jsdoc = "";
|
||||||
|
let incompleteComment = "";
|
||||||
|
for (let line of lines) {
|
||||||
|
if (/^\s*\/\*/.test(line)) {
|
||||||
|
isInComment = true;
|
||||||
|
incompleteComment += `\n${line}`;
|
||||||
|
} else if (/^\s*\*\//.test(line)) {
|
||||||
|
isInComment = false;
|
||||||
|
incompleteComment += `\n${line}`;
|
||||||
|
jsdoc = incompleteComment;
|
||||||
|
incompleteComment = "";
|
||||||
|
} else if (isInComment) {
|
||||||
|
incompleteComment += `\n${line}`;
|
||||||
|
} else {
|
||||||
|
if (!jsdoc || line.trim() === "") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isTool) {
|
||||||
|
if (/function main/.test(line)) {
|
||||||
|
output.push({
|
||||||
|
funcName: "main",
|
||||||
|
jsdoc,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const match = /function *([_A-Za-z]+)/.exec(line);
|
||||||
|
if (match) {
|
||||||
|
const funcName = match[1];
|
||||||
|
if (!funcName.startsWith("_")) {
|
||||||
|
output.push({ funcName, jsdoc });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jsdoc = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} jsdoc
|
||||||
|
* @param {string} funcName,
|
||||||
|
*/
|
||||||
|
function parseJsDoc(jsdoc, funcName) {
|
||||||
|
const lines = jsdoc.split("\n");
|
||||||
|
let description = "";
|
||||||
|
const rawParams = [];
|
||||||
|
let tag = "";
|
||||||
|
for (let line of lines) {
|
||||||
|
line = line.replace(/^\s*(\/\*\*|\*\/|\*)/, "").trim();
|
||||||
|
let match = /^@(\w+)/.exec(line);
|
||||||
|
if (match) {
|
||||||
|
tag = match[1];
|
||||||
|
}
|
||||||
|
if (!tag) {
|
||||||
|
description += `\n${line}`;
|
||||||
|
} else if (tag == "property") {
|
||||||
|
if (match) {
|
||||||
|
rawParams.push(line.slice(tag.length + 1).trim());
|
||||||
|
} else {
|
||||||
|
rawParams[rawParams.length - 1] += `\n${line}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const params = [];
|
||||||
|
for (const rawParam of rawParams) {
|
||||||
|
try {
|
||||||
|
params.push(parseParam(rawParam));
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(
|
||||||
|
`Unable to parse function '${funcName}' of jsdoc '@property ${rawParam}'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
description: description.trim(),
|
||||||
|
params,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {ReturnType<parseParam>} Param
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} rawParam
|
||||||
|
*/
|
||||||
|
function parseParam(rawParam) {
|
||||||
|
const regex = /^{([^}]+)} +(\S+)( *- +| +)?/;
|
||||||
|
const match = regex.exec(rawParam);
|
||||||
|
if (!match) {
|
||||||
|
throw new Error(`Invalid jsdoc comment`);
|
||||||
|
}
|
||||||
|
const type = match[1];
|
||||||
|
let name = match[2];
|
||||||
|
const description = rawParam.replace(regex, "");
|
||||||
|
|
||||||
|
let required = true;
|
||||||
|
if (/^\[.*\]$/.test(name)) {
|
||||||
|
name = name.slice(1, -1);
|
||||||
|
required = false;
|
||||||
|
}
|
||||||
|
let property = buildProperty(type, description);
|
||||||
|
return { name, property, required };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} type
|
||||||
|
* @param {string} description
|
||||||
|
*/
|
||||||
|
function buildProperty(type, description) {
|
||||||
|
type = type.toLowerCase();
|
||||||
|
const property = {};
|
||||||
|
if (type.includes("|")) {
|
||||||
|
property.type = "string";
|
||||||
|
property.enum = type.replace(/'/g, "").split("|");
|
||||||
|
} else if (type === "boolean") {
|
||||||
|
property.type = "boolean";
|
||||||
|
} else if (type === "string") {
|
||||||
|
property.type = "string";
|
||||||
|
} else if (type === "integer") {
|
||||||
|
property.type = "integer";
|
||||||
|
} else if (type === "number") {
|
||||||
|
property.type = "number";
|
||||||
|
} else if (type === "string[]") {
|
||||||
|
property.type = "array";
|
||||||
|
property.items = { type: "string" };
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unsupported type '${type}'`);
|
||||||
|
}
|
||||||
|
property.description = description;
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} filePath
|
||||||
|
*/
|
||||||
|
function getBasename(filePath) {
|
||||||
|
const filenameWithExt = filePath.split(/[/\\]/).pop();
|
||||||
|
|
||||||
|
const lastDotIndex = filenameWithExt.lastIndexOf(".");
|
||||||
|
|
||||||
|
if (lastDotIndex === -1) {
|
||||||
|
return filenameWithExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return filenameWithExt.substring(0, lastDotIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @param {string} description
|
||||||
|
* @param {Param[]} params
|
||||||
|
*/
|
||||||
|
function buildDeclaration(name, description, params) {
|
||||||
|
const schema = {
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
properties: {},
|
||||||
|
};
|
||||||
|
const requiredParams = [];
|
||||||
|
for (const { name, property, required } of params) {
|
||||||
|
schema.properties[name] = property;
|
||||||
|
if (required) {
|
||||||
|
requiredParams.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (requiredParams.length > 0) {
|
||||||
|
schema.required = requiredParams;
|
||||||
|
}
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import ast
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
scriptfile = sys.argv[1]
|
||||||
|
with open(scriptfile, "r", encoding="utf-8") as f:
|
||||||
|
contents = f.read()
|
||||||
|
|
||||||
|
functions = extract_functions(contents)
|
||||||
|
declarations = []
|
||||||
|
for function in functions:
|
||||||
|
func_name, docstring, func_args = function
|
||||||
|
description, params = parse_docstring(docstring)
|
||||||
|
declarations.append(
|
||||||
|
build_declaration(func_name, description, params, func_args)
|
||||||
|
)
|
||||||
|
|
||||||
|
name = os.path.splitext(os.path.basename(scriptfile))[0]
|
||||||
|
if declarations:
|
||||||
|
declarations = declarations[0:1]
|
||||||
|
declarations[0]["name"] = name
|
||||||
|
|
||||||
|
print(json.dumps(declarations, indent=2))
|
||||||
|
|
||||||
|
|
||||||
|
def extract_functions(contents: str):
|
||||||
|
tree = ast.parse(contents)
|
||||||
|
output = []
|
||||||
|
for node in ast.walk(tree):
|
||||||
|
if not isinstance(node, ast.FunctionDef):
|
||||||
|
continue
|
||||||
|
func_name = node.name
|
||||||
|
if func_name.startswith("_"):
|
||||||
|
continue
|
||||||
|
docstring = ast.get_docstring(node) or ""
|
||||||
|
func_args = OrderedDict()
|
||||||
|
for arg in node.args.args:
|
||||||
|
arg_name = arg.arg
|
||||||
|
arg_type = get_arg_type(arg.annotation)
|
||||||
|
func_args[arg_name] = arg_type
|
||||||
|
output.append((func_name, docstring, func_args))
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def get_arg_type(annotation) -> str:
|
||||||
|
if annotation is None:
|
||||||
|
return ""
|
||||||
|
elif isinstance(annotation, ast.Name):
|
||||||
|
return annotation.id
|
||||||
|
elif isinstance(annotation, ast.Subscript):
|
||||||
|
if isinstance(annotation.value, ast.Name):
|
||||||
|
type_name = annotation.value.id
|
||||||
|
if type_name == "List":
|
||||||
|
child = get_arg_type(annotation.slice)
|
||||||
|
return f"list[{child}]"
|
||||||
|
if type_name == "Literal":
|
||||||
|
literals = [ast.unparse(el) for el in annotation.slice.elts]
|
||||||
|
return f"{'|'.join(literals)}"
|
||||||
|
if type_name == "Optional":
|
||||||
|
child = get_arg_type(annotation.slice)
|
||||||
|
return f"{child}?"
|
||||||
|
return "any"
|
||||||
|
|
||||||
|
|
||||||
|
def parse_docstring(docstring: str):
|
||||||
|
lines = docstring.splitlines()
|
||||||
|
description = ""
|
||||||
|
rawParams = []
|
||||||
|
is_in_args = False
|
||||||
|
for line in lines:
|
||||||
|
if not is_in_args:
|
||||||
|
if line.startswith("Args:"):
|
||||||
|
is_in_args = True
|
||||||
|
else:
|
||||||
|
description += f"\n{line}"
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
if re.search(r"^\s+", line):
|
||||||
|
rawParams.append(line.strip())
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
params = {}
|
||||||
|
for rawParam in rawParams:
|
||||||
|
name, type_, description = parse_param(rawParam)
|
||||||
|
params[name] = (type_, description)
|
||||||
|
return (description.strip(), params)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_param(raw_param: str):
|
||||||
|
name = ""
|
||||||
|
description = ""
|
||||||
|
type_from_comment = ""
|
||||||
|
if ":" in raw_param:
|
||||||
|
name, description = raw_param.split(":", 1)
|
||||||
|
name = name.strip()
|
||||||
|
description = description.strip()
|
||||||
|
else:
|
||||||
|
name = raw_param
|
||||||
|
if " " in name:
|
||||||
|
name, type_from_comment = name.split(" ", 1)
|
||||||
|
type_from_comment = type_from_comment.strip()
|
||||||
|
|
||||||
|
if type_from_comment.startswith("(") and type_from_comment.endswith(")"):
|
||||||
|
type_from_comment = type_from_comment[1:-1]
|
||||||
|
type_parts = [value.strip() for value in type_from_comment.split(",")]
|
||||||
|
type_ = type_parts[0]
|
||||||
|
if "optional" in type_parts[1:]:
|
||||||
|
type_ = f"{type_}?"
|
||||||
|
|
||||||
|
return (name, type_, description)
|
||||||
|
|
||||||
|
|
||||||
|
def build_declaration(
|
||||||
|
name: str, description: str, params: dict, args: OrderedDict[str, str]
|
||||||
|
) -> dict[str, dict]:
|
||||||
|
schema = {
|
||||||
|
"name": name,
|
||||||
|
"description": description,
|
||||||
|
"properties": {},
|
||||||
|
}
|
||||||
|
required_params = []
|
||||||
|
for arg_name, arg_type in args.items():
|
||||||
|
type_ = arg_type
|
||||||
|
description = ""
|
||||||
|
required = True
|
||||||
|
if params.get(arg_name):
|
||||||
|
param_type, description = params[arg_name]
|
||||||
|
if not type_:
|
||||||
|
type_ = param_type
|
||||||
|
if type_.endswith("?"):
|
||||||
|
type_ = type_[:-1]
|
||||||
|
required = False
|
||||||
|
try:
|
||||||
|
property = build_property(type_, description)
|
||||||
|
except:
|
||||||
|
raise ValueError(f"Unable to parse arg '{arg_name}' of function '{name}'")
|
||||||
|
schema["properties"][arg_name] = property
|
||||||
|
if required:
|
||||||
|
required_params.append(arg_name)
|
||||||
|
if required_params:
|
||||||
|
schema["required"] = required_params
|
||||||
|
return schema
|
||||||
|
|
||||||
|
|
||||||
|
def build_property(type_: str, description: str):
|
||||||
|
property = {}
|
||||||
|
if "|" in type_:
|
||||||
|
property["type"] = "string"
|
||||||
|
property["enum"] = type_.replace("'", "").split("|")
|
||||||
|
elif type_ == "bool":
|
||||||
|
property["type"] = "boolean"
|
||||||
|
elif type_ == "str":
|
||||||
|
property["type"] = "string"
|
||||||
|
elif type_ == "int":
|
||||||
|
property["type"] = "integer"
|
||||||
|
elif type_ == "float":
|
||||||
|
property["type"] = "number"
|
||||||
|
elif type_ == "list[str]":
|
||||||
|
property["type"] = "array"
|
||||||
|
property["items"] = {"type": "string"}
|
||||||
|
elif type_ == "":
|
||||||
|
property["type"] = "string"
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unsupported type `{type_}`")
|
||||||
|
property["description"] = description
|
||||||
|
return property
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Executable
+43
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
argc --argc-export "$1" | \
|
||||||
|
jq -r '
|
||||||
|
def parse_description(flag_option):
|
||||||
|
if flag_option.describe == "" then
|
||||||
|
{}
|
||||||
|
else
|
||||||
|
{ "description": flag_option.describe }
|
||||||
|
end;
|
||||||
|
|
||||||
|
def parse_enum(flag_option):
|
||||||
|
if flag_option.choice.type == "Values" then
|
||||||
|
{ "enum": flag_option.choice.data }
|
||||||
|
else
|
||||||
|
{}
|
||||||
|
end;
|
||||||
|
|
||||||
|
def parse_property(flag_option):
|
||||||
|
[
|
||||||
|
{ condition: (flag_option.flag == true), result: { type: "boolean" } },
|
||||||
|
{ condition: (flag_option.multiple_occurs == true), result: { type: "array", items: { type: "string" } } },
|
||||||
|
{ condition: (flag_option.notations[0] == "INT"), result: { type: "integer" } },
|
||||||
|
{ condition: (flag_option.notations[0] == "NUM"), result: { type: "number" } },
|
||||||
|
{ condition: true, result: { type: "string" } } ]
|
||||||
|
| map(select(.condition) | .result) | first
|
||||||
|
| (. + parse_description(flag_option))
|
||||||
|
| (. + parse_enum(flag_option))
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
def parse_parameter(flag_options):
|
||||||
|
{
|
||||||
|
type: "object",
|
||||||
|
properties: (reduce flag_options[] as $item ({}; . + { ($item.id | sub("-"; "_"; "g")): parse_property($item) })),
|
||||||
|
required: [flag_options[] | select(.required == true) | .id | sub("-"; "_"; "g")],
|
||||||
|
};
|
||||||
|
|
||||||
|
[{
|
||||||
|
name: (.name | sub("-"; "_"; "g")),
|
||||||
|
description: .describe,
|
||||||
|
parameters: parse_parameter([.flag_options[] | select(.id != "help" and .id != "version")])
|
||||||
|
}]'
|
||||||
+84
-14
@@ -1,17 +1,20 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# @describe Create a boilplate tool script file.
|
# @describe Create a boilplate tool scriptfile.
|
||||||
|
#
|
||||||
# It automatically generate declaration json for `*.py` and `*.js` and generate `@option` tags for `.sh`.
|
# It automatically generate declaration json for `*.py` and `*.js` and generate `@option` tags for `.sh`.
|
||||||
# Examples:
|
# Examples:
|
||||||
# argc create abc.sh foo bar! baz+ qux*
|
# argc create abc.sh foo bar! baz+ qux*
|
||||||
# ./scripts/create-tool.sh test.py foo bar! baz+ qux*
|
# ./scripts/create-tool.sh test.py foo bar! baz+ qux*
|
||||||
|
#
|
||||||
|
# @flag --force Override the exist tool file
|
||||||
# @arg name! The script file name.
|
# @arg name! The script file name.
|
||||||
# @arg params* The script parameters
|
# @arg params* The script parameters
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
output="tools/$argc_name"
|
output="tools/$argc_name"
|
||||||
if [[ -f "$output" ]]; then
|
if [[ -f "$output" ]] && [[ -z "$argc_force" ]]; then
|
||||||
_die "$output already exists"
|
_die "$output already exists"
|
||||||
fi
|
fi
|
||||||
ext="${argc_name##*.}"
|
ext="${argc_name##*.}"
|
||||||
@@ -25,6 +28,7 @@ main() {
|
|||||||
py) create_py ;;
|
py) create_py ;;
|
||||||
*) _die "Invalid extension name: $ext, must be one of ${support_exts[*]}" ;;
|
*) _die "Invalid extension name: $ext, must be one of ${support_exts[*]}" ;;
|
||||||
esac
|
esac
|
||||||
|
_die "$output generated"
|
||||||
}
|
}
|
||||||
|
|
||||||
create_sh() {
|
create_sh() {
|
||||||
@@ -49,25 +53,91 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
create_js() {
|
create_js() {
|
||||||
|
properties=''
|
||||||
|
for param in "${argc_params[@]}"; do
|
||||||
|
if [[ "$param" == *'!' ]]; then
|
||||||
|
param="${param:0:$((${#param}-1))}"
|
||||||
|
property=" * @property {string} $param - "
|
||||||
|
elif [[ "$param" == *'+' ]]; then
|
||||||
|
param="${param:0:$((${#param}-1))}"
|
||||||
|
property=" * @property {string[]} $param - "
|
||||||
|
elif [[ "$param" == *'*' ]]; then
|
||||||
|
param="${param:0:$((${#param}-1))}"
|
||||||
|
property=" * @property {string[]} [$param] - "
|
||||||
|
else
|
||||||
|
property=" * @property {string} [$param] - "
|
||||||
|
fi
|
||||||
|
properties+=$'\n'"$property"
|
||||||
|
done
|
||||||
cat <<EOF > "$output"
|
cat <<EOF > "$output"
|
||||||
exports.declarate = function declarate() {
|
/**
|
||||||
return $(build_schema)
|
*
|
||||||
}
|
* @typedef {Object} Args${properties}
|
||||||
|
* @param {Args} args
|
||||||
exports.execute = function execute(data) {
|
*/
|
||||||
console.log(data)
|
exports.main = function main(args) {
|
||||||
|
console.log(args);
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
create_py() {
|
create_py() {
|
||||||
|
has_array_param=false
|
||||||
|
has_optional_pram=false
|
||||||
|
required_properties=''
|
||||||
|
optional_properties=''
|
||||||
|
required_arguments=()
|
||||||
|
optional_arguments=()
|
||||||
|
indent=" "
|
||||||
|
for param in "${argc_params[@]}"; do
|
||||||
|
optional=false
|
||||||
|
if [[ "$param" == *'!' ]]; then
|
||||||
|
param="${param:0:$((${#param}-1))}"
|
||||||
|
type="str"
|
||||||
|
elif [[ "$param" == *'+' ]]; then
|
||||||
|
param="${param:0:$((${#param}-1))}"
|
||||||
|
type="List[str]"
|
||||||
|
has_array_param=true
|
||||||
|
elif [[ "$param" == *'*' ]]; then
|
||||||
|
param="${param:0:$((${#param}-1))}"
|
||||||
|
type="Optional[List[str]] = None"
|
||||||
|
optional=true
|
||||||
|
has_array_param=true
|
||||||
|
else
|
||||||
|
optional=true
|
||||||
|
type="Optional[str] = None"
|
||||||
|
fi
|
||||||
|
if [[ "$optional" == "true" ]]; then
|
||||||
|
has_optional_pram=true
|
||||||
|
optional_arguments+="$param: $type, "
|
||||||
|
optional_properties+=$'\n'"$indent$indent$param: -"
|
||||||
|
else
|
||||||
|
required_arguments+="$param: $type, "
|
||||||
|
required_properties+=$'\n'"$indent$indent$param: -"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
import_typing_members=()
|
||||||
|
if [[ "$has_array_param" == "true" ]]; then
|
||||||
|
import_typing_members+=("List")
|
||||||
|
fi
|
||||||
|
if [[ "$has_optional_pram" == "true" ]]; then
|
||||||
|
import_typing_members+=("Optional")
|
||||||
|
fi
|
||||||
|
imports=""
|
||||||
|
if [[ -n "$import_typing_members" ]]; then
|
||||||
|
members="$(echo "${import_typing_members[*]}" | sed 's/ /, /')"
|
||||||
|
imports="from typing import $members"$'\n'
|
||||||
|
fi
|
||||||
|
if [[ -n "$imports" ]]; then
|
||||||
|
imports="$imports"$'\n'
|
||||||
|
fi
|
||||||
cat <<EOF > "$output"
|
cat <<EOF > "$output"
|
||||||
def declarate():
|
${imports}
|
||||||
return $(build_schema)
|
def main(${required_arguments}${optional_arguments}):
|
||||||
|
"""
|
||||||
|
Args:${required_properties}${optional_properties}
|
||||||
def execute(data):
|
"""
|
||||||
print(data)
|
pass
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-13
@@ -58,23 +58,18 @@ const [funcName, funcData] = parseArgv();
|
|||||||
|
|
||||||
process.env["LLM_FUNCTION_NAME"] = funcName;
|
process.env["LLM_FUNCTION_NAME"] = funcName;
|
||||||
|
|
||||||
if (process.env["LLM_FUNCTION_ACTION"] == "declarate") {
|
if (!funcData) {
|
||||||
const { declarate } = loadFunc(funcName);
|
|
||||||
console.log(JSON.stringify(declarate(), null, 2));
|
|
||||||
} else {
|
|
||||||
if (!funcData) {
|
|
||||||
console.log("No json data");
|
console.log("No json data");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let args;
|
let args;
|
||||||
try {
|
try {
|
||||||
args = JSON.parse(funcData);
|
args = JSON.parse(funcData);
|
||||||
} catch {
|
} catch {
|
||||||
console.log("Invalid json data");
|
console.log("Invalid json data");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
|
||||||
|
|
||||||
const { execute } = loadFunc(funcName);
|
|
||||||
execute(args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { main } = loadFunc(funcName);
|
||||||
|
main(args);
|
||||||
|
|||||||
+17
-15
@@ -5,6 +5,7 @@ import json
|
|||||||
import sys
|
import sys
|
||||||
import importlib.util
|
import importlib.util
|
||||||
|
|
||||||
|
|
||||||
def parse_argv():
|
def parse_argv():
|
||||||
func_name = sys.argv[0]
|
func_name = sys.argv[0]
|
||||||
func_data = None
|
func_data = None
|
||||||
@@ -21,6 +22,7 @@ def parse_argv():
|
|||||||
|
|
||||||
return func_name, func_data
|
return func_name, func_data
|
||||||
|
|
||||||
|
|
||||||
def load_func(func_name):
|
def load_func(func_name):
|
||||||
func_file_name = f"{func_name}.py"
|
func_file_name = f"{func_name}.py"
|
||||||
func_path = os.path.join(os.environ["LLM_FUNCTIONS_DIR"], f"tools/{func_file_name}")
|
func_path = os.path.join(os.environ["LLM_FUNCTIONS_DIR"], f"tools/{func_file_name}")
|
||||||
@@ -33,20 +35,24 @@ def load_func(func_name):
|
|||||||
print(f"Invalid function: {func_file_name}")
|
print(f"Invalid function: {func_file_name}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def load_env(file_path):
|
def load_env(file_path):
|
||||||
try:
|
try:
|
||||||
with open(file_path, 'r') as f:
|
with open(file_path, "r") as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if line.startswith('#') or line == '':
|
if line.startswith("#") or line == "":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
key, *value = line.split('=')
|
key, *value = line.split("=")
|
||||||
os.environ[key.strip()] = '='.join(value).strip()
|
os.environ[key.strip()] = "=".join(value).strip()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
os.environ["LLM_FUNCTIONS_DIR"] = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
|
||||||
|
os.environ["LLM_FUNCTIONS_DIR"] = os.path.abspath(
|
||||||
|
os.path.join(os.path.dirname(__file__), "..")
|
||||||
|
)
|
||||||
|
|
||||||
load_env(os.path.join(os.environ["LLM_FUNCTIONS_DIR"], ".env"))
|
load_env(os.path.join(os.environ["LLM_FUNCTIONS_DIR"], ".env"))
|
||||||
|
|
||||||
@@ -54,20 +60,16 @@ func_name, func_data = parse_argv()
|
|||||||
|
|
||||||
os.environ["LLM_FUNCTION_NAME"] = func_name
|
os.environ["LLM_FUNCTION_NAME"] = func_name
|
||||||
|
|
||||||
if os.getenv("LLM_FUNCTION_ACTION") == "declarate":
|
if not func_data:
|
||||||
module = load_func(func_name)
|
|
||||||
print(json.dumps(module.declarate(), indent=2))
|
|
||||||
else:
|
|
||||||
if not func_data:
|
|
||||||
print("No json data")
|
print("No json data")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
args = None
|
args = None
|
||||||
try:
|
try:
|
||||||
args = json.loads(func_data)
|
args = json.loads(func_data)
|
||||||
except (json.JSONDecodeError, TypeError):
|
except (json.JSONDecodeError, TypeError):
|
||||||
print("Invalid json data")
|
print("Invalid json data")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
module = load_func(func_name)
|
module = load_func(func_name)
|
||||||
module.execute(args)
|
module.main(**args)
|
||||||
|
|||||||
+8
-52
@@ -27,55 +27,12 @@ if [[ "$OS" == "Windows_NT" ]]; then
|
|||||||
func_file="$(cygpath -w "$func_file")"
|
func_file="$(cygpath -w "$func_file")"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$LLM_FUNCTION_ACTION" == "declarate" ]]; then
|
if [[ -z "$func_data" ]]; then
|
||||||
argc --argc-export "$func_file" | \
|
|
||||||
$JQ -r '
|
|
||||||
def parse_description(flag_option):
|
|
||||||
if flag_option.describe == "" then
|
|
||||||
{}
|
|
||||||
else
|
|
||||||
{ "description": flag_option.describe }
|
|
||||||
end;
|
|
||||||
|
|
||||||
def parse_enum(flag_option):
|
|
||||||
if flag_option.choice.type == "Values" then
|
|
||||||
{ "enum": flag_option.choice.data }
|
|
||||||
else
|
|
||||||
{}
|
|
||||||
end;
|
|
||||||
|
|
||||||
def parse_property(flag_option):
|
|
||||||
[
|
|
||||||
{ condition: (flag_option.flag == true), result: { type: "boolean" } },
|
|
||||||
{ condition: (flag_option.multiple_occurs == true), result: { type: "array", items: { type: "string" } } },
|
|
||||||
{ condition: (flag_option.notations[0] == "INT"), result: { type: "integer" } },
|
|
||||||
{ condition: (flag_option.notations[0] == "NUM"), result: { type: "number" } },
|
|
||||||
{ condition: true, result: { type: "string" } } ]
|
|
||||||
| map(select(.condition) | .result) | first
|
|
||||||
| (. + parse_description(flag_option))
|
|
||||||
| (. + parse_enum(flag_option))
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
def parse_parameter(flag_options):
|
|
||||||
{
|
|
||||||
type: "object",
|
|
||||||
properties: (reduce flag_options[] as $item ({}; . + { ($item.id | sub("-"; "_"; "g")): parse_property($item) })),
|
|
||||||
required: [flag_options[] | select(.required == true) | .id],
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
name: (.name | sub("-"; "_"; "g")),
|
|
||||||
description: .describe,
|
|
||||||
parameters: parse_parameter([.flag_options[] | select(.id != "help" and .id != "version")])
|
|
||||||
}'
|
|
||||||
else
|
|
||||||
if [[ -z "$func_data" ]]; then
|
|
||||||
echo "No json data"
|
echo "No json data"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
data="$(
|
data="$(
|
||||||
echo "$func_data" | \
|
echo "$func_data" | \
|
||||||
$JQ -r '
|
$JQ -r '
|
||||||
to_entries | .[] |
|
to_entries | .[] |
|
||||||
@@ -87,16 +44,15 @@ else
|
|||||||
else
|
else
|
||||||
"--\($key)\n\(.value | @json)"
|
"--\($key)\n\(.value | @json)"
|
||||||
end'
|
end'
|
||||||
)" || {
|
)" || {
|
||||||
echo "Invalid json data"
|
echo "Invalid json data"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
if [[ "$line" == '--'* ]]; then
|
if [[ "$line" == '--'* ]]; then
|
||||||
args+=("$line")
|
args+=("$line")
|
||||||
else
|
else
|
||||||
args+=("$(echo "$line" | $JQ -r '.')")
|
args+=("$(echo "$line" | $JQ -r '.')")
|
||||||
fi
|
fi
|
||||||
done <<< "$data"
|
done <<< "$data"
|
||||||
"$func_file" "${args[@]}"
|
"$func_file" "${args[@]}"
|
||||||
fi
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* 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.main = function main(args) {
|
||||||
|
console.log(args);
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
from typing import List, Literal, Optional
|
||||||
|
|
||||||
|
|
||||||
|
def main(
|
||||||
|
boolean: bool,
|
||||||
|
string: str,
|
||||||
|
string_enum: Literal["foo", "bar"],
|
||||||
|
integer: int,
|
||||||
|
number: float,
|
||||||
|
array: List[str],
|
||||||
|
string_optional: Optional[str] = None,
|
||||||
|
array_optional: Optional[List[str]] = None,
|
||||||
|
) -> None:
|
||||||
|
"""Demonstrate how to create a tool using Python and how to use comments.
|
||||||
|
Args:
|
||||||
|
boolean: Define a required boolean property
|
||||||
|
string: Define a required string property
|
||||||
|
string_enum: Define a required string property with enum
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
print(f"boolean: {boolean}")
|
||||||
|
print(f"string: {string}")
|
||||||
|
print(f"string_enum: {string_enum}")
|
||||||
|
print(f"integer: {integer}")
|
||||||
|
print(f"number: {number}")
|
||||||
|
print(f"array: {array}")
|
||||||
|
print(f"string_optional: {string_optional}")
|
||||||
|
print(f"array_optional: {array_optional}")
|
||||||
Executable
+15
@@ -0,0 +1,15 @@
|
|||||||
|
# @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
|
||||||
|
|
||||||
|
main() {
|
||||||
|
( set -o posix ; set ) | grep ^argc_ # inspect all argc variables
|
||||||
|
}
|
||||||
|
|
||||||
|
eval "$(argc --argc-eval "$0" "$@")"
|
||||||
@@ -1,22 +1,9 @@
|
|||||||
exports.declarate = function declarate() {
|
/**
|
||||||
return {
|
* Runs the javascript code in node.js.
|
||||||
"name": "may_execute_js_code",
|
* @typedef {Object} Args
|
||||||
"description": "Runs the javascript code in node.js.",
|
* @property {string} code - Javascript code to execute, such as `console.log("hello world")`
|
||||||
"parameters": {
|
* @param {Args} args
|
||||||
"type": "object",
|
*/
|
||||||
"properties": {
|
exports.main = function main({ code }) {
|
||||||
"code": {
|
eval(code);
|
||||||
"type": "string",
|
|
||||||
"description": "Javascript code to execute, such as `console.log(\"hello world\")`"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.execute = function execute(data) {
|
|
||||||
eval(data.code)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,6 @@
|
|||||||
def declarate():
|
def main(code: str):
|
||||||
return {
|
"""Runs the python code.
|
||||||
"name": "may_execute_py_code",
|
Args:
|
||||||
"description": "Runs the python code.",
|
code: Python code to execute, such as `print("hello world")`
|
||||||
"parameters": {
|
"""
|
||||||
"type": "object",
|
exec(code)
|
||||||
"properties": {
|
|
||||||
"code": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Python code to execute, such as `print(\"hello world\")`"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def execute(data):
|
|
||||||
exec(data["code"])
|
|
||||||
|
|||||||
Reference in New Issue
Block a user