Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: improve Argcfile.sh and scripts/run-tool* #34

Merged
merged 2 commits into from
Jun 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 38 additions & 28 deletions Argcfile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ LANG_CMDS=( \
)

# @cmd Run the tool
# @arg cmd![`_choice_cmd`] The function command
# @alias call
# @arg cmd![`_choice_cmd`] The tool command
# @arg json The json data
run-tool() {
if _is_win; then
Expand All @@ -22,76 +23,81 @@ run-tool() {
}

# @cmd Build the project
build() {
argc build-tools
}

# @cmd Build tools
# @option --names-file=functions.txt Path to a file containing tool filenames, one per line.
# This file specifies which tools will be used.
# @option --declarations-file=functions.json <FILE> Path to a json file to save function declarations
# This file specifies which function files to build.
# Example:
# get_current_weather.sh
# may_execute_js_code.js
build() {
argc build-declarations-json --names-file "${argc_names_file}" --declarations-file "${argc_declarations_file}"
argc build-bin --names-file "${argc_names_file}"
build-tools() {
argc build-tools-json --names-file "${argc_names_file}" --declarations-file "${argc_declarations_file}"
argc build-tools-bin --names-file "${argc_names_file}"
}

# @cmd Build tool binaries
# @cmd Build tools to bin
# @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-tools-bin() {
mkdir -p "$BIN_DIR"
if [[ "${#argc_tools[@]}" -gt 0 ]]; then
names=("${argc_tools[@]}" )
elif [[ -f "$argc_names_file" ]]; then
names=($(cat "$argc_names_file"))
if [[ "${#names[@]}" -gt 0 ]]; then
(cd "$BIN_DIR" && rm -rf "${names[@]}")
fi
fi
if [[ -z "$names" ]]; then
_die "error: no tools selected"
_die "error: not input tools, not found '$argc_names_file', please create it add some tools."
fi
mkdir -p "$BIN_DIR"
rm -rf "$BIN_DIR"/*
not_found_tools=()
for name in "${names[@]}"; do
basename="${name%.*}"
lang="${name##*.}"
func_file="tools/$name"
if [[ -f "$func_file" ]]; then
tool_path="tools/$name"
if [[ -f "$tool_path" ]]; then
if _is_win; then
bin_file="$BIN_DIR/$basename.cmd"
_build_win_shim $lang > "$bin_file"
_build_win_shim_tool $lang > "$bin_file"
else
bin_file="$BIN_DIR/$basename"
ln -s -f "$PWD/scripts/run-tool.$lang" "$bin_file"
fi
echo "Build tool $name"
else
not_found_tools+=("$name")
fi
done
if [[ -n "$not_found_tools" ]]; then
_die "error: not found tools: ${not_found_tools[*]}"
fi
for name in "$BIN_DIR"/*; do
echo "Build $name"
done
}

# @cmd Build declarations.json
# @cmd Build tool functions.json
# @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
# @arg tools*[`_choice_tool`] The tool filenames
build-declarations-json() {
build-tools-json() {
if [[ "${#argc_tools[@]}" -gt 0 ]]; then
names=("${argc_tools[@]}" )
elif [[ -f "$argc_names_file" ]]; then
names=($(cat "$argc_names_file"))
fi
if [[ -z "$names" ]]; then
_die "error: no tools selected"
_die "error: not input tools, not found '$argc_names_file', please create it add some tools."
fi
json_list=()
not_found_tools=()
build_failed_tools=()
for name in "${names[@]}"; do
lang="${name##*.}"
func_file="tools/$name"
if [[ ! -f "$func_file" ]]; then
tool_path="tools/$name"
if [[ ! -f "$tool_path" ]]; then
not_found_tools+=("$name")
continue;
fi
Expand Down Expand Up @@ -131,15 +137,19 @@ test() {
test-tools
}

# @cmd Test call functions
# @cmd Test tools
test-tools() {
tmp_dir="cache/tmp"
mkdir -p "$tmp_dir"
names_file="$tmp_dir/functions.txt"
declarations_file="$tmp_dir/functions.json"
argc list-tools > "$names_file"
argc build --names-file "$names_file" --declarations-file "$declarations_file"
argc build-tools --names-file "$names_file" --declarations-file "$declarations_file"
test-tools-execute-lang
}

# @cmd Test maybe_execute_* tools
test-tools-execute-lang() {
if _is_win; then
ext=".cmd"
fi
Expand All @@ -152,8 +162,8 @@ test-tools() {
for test_case in "${test_cases[@]}"; do
IFS='#' read -r lang tool_name data <<<"${test_case}"
cmd="$(_lang_to_cmd "$lang")"
cmd_path="$BIN_DIR/$tool_name$ext"
if command -v "$cmd" &> /dev/null; then
cmd_path="$BIN_DIR/$tool_name$ext"
echo -n "Test $cmd_path: "
"$cmd_path" "$data"
if ! _is_win; then
Expand All @@ -164,12 +174,12 @@ test-tools() {
done
}

# @cmd Test all demo tools
test-demo-tools() {
# @cmd Test demo tools
test-tools-demo() {
for item in "${LANG_CMDS[@]}"; do
lang="${item%:*}"
echo "---- Test demo_tool.$lang ---"
argc build-bin "demo_tool.$lang"
argc build-tools-bin "demo_tool.$lang"
argc run-tool demo_tool '{
"boolean": true,
"string": "Hello",
Expand Down Expand Up @@ -236,7 +246,7 @@ _lang_to_cmd() {
done
}

_build_win_shim() {
_build_win_shim_tool() {
lang="$1"
cmd="$(_lang_to_cmd "$lang")"
if [[ "$lang" == "sh" ]]; then
Expand Down
101 changes: 65 additions & 36 deletions scripts/run-tool.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,24 @@

const path = require("path");
const fs = require("fs");
const os = require("os");

function parseArgv() {
async function main() {
const [toolName, rawData] = parseArgv("run-tool.js");
const toolData = parseRawData(rawData);

const rootDir = path.resolve(__dirname, "..");
setupEnv(rootDir, toolName);

const toolPath = path.resolve(rootDir, `tools/${toolName}.js`);
await run(toolPath, "run", toolData);
}

function parseArgv(thisFileName) {
let toolName = process.argv[1];
let toolData = null;

if (toolName.endsWith("run-tool.js")) {
if (toolName.endsWith(thisFileName)) {
toolName = process.argv[2];
toolData = process.argv[3];
} else {
Expand All @@ -22,20 +34,24 @@ function parseArgv() {
return [toolName, toolData];
}

function loadModule(toolName) {
const toolFileName = `${toolName}.js`;
const toolPath = path.resolve(
process.env["LLM_ROOT_DIR"],
`tools/${toolFileName}`,
);
function parseRawData(data) {
if (!data) {
throw new Error("No JSON data");
}
try {
return require(toolPath);
return JSON.parse(data);
} catch {
console.log(`Invalid tooltion: ${toolFileName}`);
process.exit(1);
throw new Error("Invalid JSON data");
}
}

function setupEnv(rootDir, toolName) {
process.env["LLM_ROOT_DIR"] = rootDir;
loadEnv(path.resolve(rootDir, ".env"));
process.env["LLM_TOOL_NAME"] = toolName;
process.env["LLM_TOOL_CACHE_DIR"] = path.resolve(rootDir, "cache", toolName);
}

function loadEnv(filePath) {
try {
const data = fs.readFileSync(filePath, "utf-8");
Expand All @@ -50,32 +66,45 @@ function loadEnv(filePath) {
} catch {}
}

const LLM_ROOT_DIR = path.resolve(__dirname, "..");
process.env["LLM_ROOT_DIR"] = LLM_ROOT_DIR;

loadEnv(path.resolve(LLM_ROOT_DIR, ".env"));

const [toolName, toolData] = parseArgv();

process.env["LLM_TOOL_NAME"] = toolName;
process.env["LLM_TOOL_CACHE_DIR"] = path.resolve(
LLM_ROOT_DIR,
"cache",
toolName,
);

if (!toolData) {
console.log("No json data");
process.exit(1);
async function run(toolPath, toolFunc, toolData) {
let mod;
if (os.platform() === "win32") {
toolPath = `file://${toolPath}`;
}
try {
mod = await import(toolPath);
} catch {
throw new Error(`Unable to load tool at '${toolPath}'`);
}
if (!mod || !mod[toolFunc]) {
throw new Error(`Not module function '${toolFunc}' at '${toolPath}'`);
}
const value = await mod[toolFunc](toolData);
dumpValue(value);
}

let data = null;
try {
data = JSON.parse(toolData);
} catch {
console.log("Invalid json data");
process.exit(1);
function dumpValue(value) {
if (value === null || value === undefined) {
return;
}
const type = typeof value;
if (type === "string" || type === "number" || type === "boolean") {
console.log(value);
} else if (type === "object") {
const proto = Object.prototype.toString.call(value);
if (proto === "[object Object]" || proto === "[object Array]") {
const valueStr = JSON.stringify(value, null, 2);
require("assert").deepStrictEqual(value, JSON.parse(valueStr));
console.log(valueStr);
}
}
}

const { run } = loadModule(toolName);
run(data);
(async () => {
try {
await main();
} catch (err) {
console.error(err?.message || err);
process.exit(1);
}
})();
Loading