fix: Tool call improvements for Windows systems
This commit is contained in:
Generated
+1
@@ -3137,6 +3137,7 @@ dependencies = [
|
|||||||
"crossterm 0.28.1",
|
"crossterm 0.28.1",
|
||||||
"dirs",
|
"dirs",
|
||||||
"duct",
|
"duct",
|
||||||
|
"dunce",
|
||||||
"fancy-regex",
|
"fancy-regex",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"fuzzy-matcher",
|
"fuzzy-matcher",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ anyhow = "1.0.69"
|
|||||||
bytes = "1.4.0"
|
bytes = "1.4.0"
|
||||||
clap = { version = "4.5.40", features = ["cargo", "derive", "wrap_help"] }
|
clap = { version = "4.5.40", features = ["cargo", "derive", "wrap_help"] }
|
||||||
dirs = "6.0.0"
|
dirs = "6.0.0"
|
||||||
|
dunce = "1.0.5"
|
||||||
futures-util = "0.3.29"
|
futures-util = "0.3.29"
|
||||||
inquire = "0.9.4"
|
inquire = "0.9.4"
|
||||||
is-terminal = "0.4.9"
|
is-terminal = "0.4.9"
|
||||||
|
|||||||
@@ -50,7 +50,13 @@ def parse_raw_data(data):
|
|||||||
|
|
||||||
def parse_argv():
|
def parse_argv():
|
||||||
agent_func = sys.argv[1]
|
agent_func = sys.argv[1]
|
||||||
agent_data = sys.argv[2]
|
|
||||||
|
tool_data_file = os.environ.get("LLM_TOOL_DATA_FILE")
|
||||||
|
if tool_data_file and os.path.isfile(tool_data_file):
|
||||||
|
with open(tool_data_file, "r", encoding="utf-8") as f:
|
||||||
|
agent_data = f.read()
|
||||||
|
else:
|
||||||
|
agent_data = sys.argv[2]
|
||||||
|
|
||||||
if (not agent_data) or (not agent_func):
|
if (not agent_data) or (not agent_func):
|
||||||
print("Usage: ./{agent_name}.py <agent-func> <agent-data>", file=sys.stderr)
|
print("Usage: ./{agent_name}.py <agent-func> <agent-data>", file=sys.stderr)
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ main() {
|
|||||||
|
|
||||||
parse_argv() {
|
parse_argv() {
|
||||||
agent_func="$1"
|
agent_func="$1"
|
||||||
agent_data="$2"
|
if [[ -n "$LLM_TOOL_DATA_FILE" ]] && [[ -f "$LLM_TOOL_DATA_FILE" ]]; then
|
||||||
|
agent_data="$(cat "$LLM_TOOL_DATA_FILE")"
|
||||||
|
else
|
||||||
|
agent_data="$2"
|
||||||
|
fi
|
||||||
if [[ -z "$agent_data" ]] || [[ -z "$agent_func" ]]; then
|
if [[ -z "$agent_data" ]] || [[ -z "$agent_func" ]]; then
|
||||||
die "usage: ./{agent_name}.sh <agent-func> <agent-data>"
|
die "usage: ./{agent_name}.sh <agent-func> <agent-data>"
|
||||||
fi
|
fi
|
||||||
@@ -57,7 +61,6 @@ run() {
|
|||||||
if [[ "$OS" == "Windows_NT" ]]; then
|
if [[ "$OS" == "Windows_NT" ]]; then
|
||||||
set -o igncr
|
set -o igncr
|
||||||
tools_path="$(cygpath -w "$tools_path")"
|
tools_path="$(cygpath -w "$tools_path")"
|
||||||
tool_data="$(echo "$tool_data" | sed 's/\\/\\\\/g')"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
jq_script="$(cat <<-'EOF'
|
jq_script="$(cat <<-'EOF'
|
||||||
|
|||||||
@@ -49,6 +49,11 @@ def parse_raw_data(data):
|
|||||||
|
|
||||||
|
|
||||||
def parse_argv():
|
def parse_argv():
|
||||||
|
tool_data_file = os.environ.get("LLM_TOOL_DATA_FILE")
|
||||||
|
if tool_data_file and os.path.isfile(tool_data_file):
|
||||||
|
with open(tool_data_file, "r", encoding="utf-8") as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
argv = sys.argv[:] + [None] * max(0, 2 - len(sys.argv))
|
argv = sys.argv[:] + [None] * max(0, 2 - len(sys.argv))
|
||||||
|
|
||||||
tool_data = argv[1]
|
tool_data = argv[1]
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parse_argv() {
|
parse_argv() {
|
||||||
tool_data="$1"
|
if [[ -n "$LLM_TOOL_DATA_FILE" ]] && [[ -f "$LLM_TOOL_DATA_FILE" ]]; then
|
||||||
|
tool_data="$(cat "$LLM_TOOL_DATA_FILE")"
|
||||||
|
else
|
||||||
|
tool_data="$1"
|
||||||
|
fi
|
||||||
if [[ -z "$tool_data" ]]; then
|
if [[ -z "$tool_data" ]]; then
|
||||||
die "usage: ./{function_name}.sh <tool-data>"
|
die "usage: ./{function_name}.sh <tool-data>"
|
||||||
fi
|
fi
|
||||||
@@ -54,7 +58,6 @@ run() {
|
|||||||
if [[ "$OS" == "Windows_NT" ]]; then
|
if [[ "$OS" == "Windows_NT" ]]; then
|
||||||
set -o igncr
|
set -o igncr
|
||||||
tool_path="$(cygpath -w "$tool_path")"
|
tool_path="$(cygpath -w "$tool_path")"
|
||||||
tool_data="$(echo "$tool_data" | sed 's/\\/\\\\/g')"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
jq_script="$(cat <<-'EOF'
|
jq_script="$(cat <<-'EOF'
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ The following variables can be used to change the log level of Loki or the locat
|
|||||||
can also pass the `--disable-log-colors` flag as well.
|
can also pass the `--disable-log-colors` flag as well.
|
||||||
|
|
||||||
## Miscellaneous Variables
|
## Miscellaneous Variables
|
||||||
| Environment Variable | Description | Default Value |
|
| Environment Variable | Description | Default Value |
|
||||||
|----------------------|--------------------------------------------------------------------------------------------------|---------------|
|
|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
|
||||||
| `AUTO_CONFIRM` | Bypass all `guard_*` checks in the bash prompt helpers; useful for agent composition and routing | |
|
| `AUTO_CONFIRM` | Bypass all `guard_*` checks in the bash prompt helpers; useful for agent composition and routing | |
|
||||||
|
| `LLM_TOOL_DATA_FILE` | Set automatically by Loki on Windows. Points to a temporary file containing the JSON tool call data. <br>Tool scripts (`run-tool.sh`, `run-agent.sh`, etc.) read from this file instead of command-line args <br>to avoid JSON escaping issues when data passes through `cmd.exe` → bash. **Not intended to be set by users.** | |
|
||||||
+29
-14
@@ -613,6 +613,7 @@ impl Functions {
|
|||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let content_template = unsafe { std::str::from_utf8_unchecked(&embedded_file.data) };
|
let content_template = unsafe { std::str::from_utf8_unchecked(&embedded_file.data) };
|
||||||
|
let to_script_path = |p: &str| -> String { p.replace('\\', "/") };
|
||||||
let content = match binary_type {
|
let content = match binary_type {
|
||||||
BinaryType::Tool(None) => {
|
BinaryType::Tool(None) => {
|
||||||
let root_dir = Config::functions_dir();
|
let root_dir = Config::functions_dir();
|
||||||
@@ -622,8 +623,8 @@ impl Functions {
|
|||||||
);
|
);
|
||||||
content_template
|
content_template
|
||||||
.replace("{function_name}", binary_name)
|
.replace("{function_name}", binary_name)
|
||||||
.replace("{root_dir}", &root_dir.to_string_lossy())
|
.replace("{root_dir}", &to_script_path(&root_dir.to_string_lossy()))
|
||||||
.replace("{tool_path}", &tool_path)
|
.replace("{tool_path}", &to_script_path(&tool_path))
|
||||||
}
|
}
|
||||||
BinaryType::Tool(Some(agent_name)) => {
|
BinaryType::Tool(Some(agent_name)) => {
|
||||||
let root_dir = Config::agent_data_dir(agent_name);
|
let root_dir = Config::agent_data_dir(agent_name);
|
||||||
@@ -633,16 +634,19 @@ impl Functions {
|
|||||||
);
|
);
|
||||||
content_template
|
content_template
|
||||||
.replace("{function_name}", binary_name)
|
.replace("{function_name}", binary_name)
|
||||||
.replace("{root_dir}", &root_dir.to_string_lossy())
|
.replace("{root_dir}", &to_script_path(&root_dir.to_string_lossy()))
|
||||||
.replace("{tool_path}", &tool_path)
|
.replace("{tool_path}", &to_script_path(&tool_path))
|
||||||
}
|
}
|
||||||
BinaryType::Agent => content_template
|
BinaryType::Agent => content_template
|
||||||
.replace("{agent_name}", binary_name)
|
.replace("{agent_name}", binary_name)
|
||||||
.replace("{config_dir}", &Config::config_dir().to_string_lossy()),
|
.replace(
|
||||||
|
"{config_dir}",
|
||||||
|
&to_script_path(&Config::config_dir().to_string_lossy()),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
.replace(
|
.replace(
|
||||||
"{prompt_utils_file}",
|
"{prompt_utils_file}",
|
||||||
&Config::bash_prompt_utils_file().to_string_lossy(),
|
&to_script_path(&Config::bash_prompt_utils_file().to_string_lossy()),
|
||||||
);
|
);
|
||||||
if binary_script_file.exists() {
|
if binary_script_file.exists() {
|
||||||
fs::remove_file(&binary_script_file)?;
|
fs::remove_file(&binary_script_file)?;
|
||||||
@@ -666,7 +670,7 @@ impl Functions {
|
|||||||
.join(".venv")
|
.join(".venv")
|
||||||
.join("Scripts")
|
.join("Scripts")
|
||||||
.join("activate.bat");
|
.join("activate.bat");
|
||||||
let canonicalized_path = fs::canonicalize(&executable_path)?;
|
let canonicalized_path = dunce::canonicalize(&executable_path)?;
|
||||||
format!(
|
format!(
|
||||||
"call \"{}\" && {}",
|
"call \"{}\" && {}",
|
||||||
canonicalized_path.to_string_lossy(),
|
canonicalized_path.to_string_lossy(),
|
||||||
@@ -677,19 +681,16 @@ impl Functions {
|
|||||||
let executable_path = which::which("python")
|
let executable_path = which::which("python")
|
||||||
.or_else(|_| which::which("python3"))
|
.or_else(|_| which::which("python3"))
|
||||||
.map_err(|_| anyhow!("Python executable not found in PATH"))?;
|
.map_err(|_| anyhow!("Python executable not found in PATH"))?;
|
||||||
let canonicalized_path = fs::canonicalize(&executable_path)?;
|
let canonicalized_path = dunce::canonicalize(&executable_path)?;
|
||||||
canonicalized_path.to_string_lossy().into_owned()
|
canonicalized_path.to_string_lossy().into_owned()
|
||||||
}
|
}
|
||||||
_ => bail!("Unsupported language: {}", language.as_ref()),
|
_ => bail!("Unsupported language: {}", language.as_ref()),
|
||||||
};
|
};
|
||||||
let bin_dir = binary_file
|
let bin_dir = binary_file
|
||||||
.parent()
|
.parent()
|
||||||
.expect("Failed to get parent directory of binary file")
|
.expect("Failed to get parent directory of binary file");
|
||||||
.canonicalize()?
|
let bin_dir = dunce::canonicalize(bin_dir)?.to_string_lossy().into_owned();
|
||||||
.to_string_lossy()
|
let wrapper_binary = dunce::canonicalize(&binary_script_file)?
|
||||||
.into_owned();
|
|
||||||
let wrapper_binary = binary_script_file
|
|
||||||
.canonicalize()?
|
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
.into_owned();
|
.into_owned();
|
||||||
let content = formatdoc!(
|
let content = formatdoc!(
|
||||||
@@ -1117,6 +1118,20 @@ pub fn run_llm_function(
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let cmd_name = polyfill_cmd_name(&cmd_name, &bin_dirs);
|
let cmd_name = polyfill_cmd_name(&cmd_name, &bin_dirs);
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let cmd_args = {
|
||||||
|
let mut args = cmd_args;
|
||||||
|
if let Some(json_data) = args.pop() {
|
||||||
|
let tool_data_file = temp_file("-tool-data-", ".json");
|
||||||
|
fs::write(&tool_data_file, &json_data)?;
|
||||||
|
envs.insert(
|
||||||
|
"LLM_TOOL_DATA_FILE".into(),
|
||||||
|
tool_data_file.display().to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
args
|
||||||
|
};
|
||||||
|
|
||||||
envs.insert("CLICOLOR_FORCE".into(), "1".into());
|
envs.insert("CLICOLOR_FORCE".into(), "1".into());
|
||||||
envs.insert("FORCE_COLOR".into(), "1".into());
|
envs.insert("FORCE_COLOR".into(), "1".into());
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user