Make the sandbox prompt match what the tools actually enforce
The terminal does not enforce a host allowlist (git and pip still reach the network), user attachments go to the RAG store rather than the sandbox workdir, and the code nudge named a possibly-disabled sibling tool. Reword the sandbox note to attribute the public allowlist to the python tool and the curl/wget block to the terminal, separate attached documents from workdir contents, make the code tip tool-neutral, and point the blocked-network message at the python egress for public hosts.
This commit is contained in:
parent
c2903da6a5
commit
04434151f9
4 changed files with 84 additions and 26 deletions
|
|
@ -2955,18 +2955,21 @@ WEB_SEARCH_TOOL = {
|
|||
_SANDBOX_PATHS_NOTE = (
|
||||
" The working directory is an isolated scratch space that may already hold "
|
||||
"files from earlier work in this conversation or project, plus anything you "
|
||||
"create here or that is uploaded; it persists across this conversation and, "
|
||||
"for a project, across the project's threads. Code execution can fetch from a "
|
||||
"fixed allowlist of public sites (such as github.com, huggingface.co, and "
|
||||
"pypi.org); shell network commands are blocked, and neither can reach the "
|
||||
"user's own machines, private hosts, or arbitrary addresses. A repository, "
|
||||
"folder, or file the user refers to is not present "
|
||||
"here unless it was uploaded, created here, or already part of this project, "
|
||||
"so list the working directory to see what is available rather than assuming "
|
||||
"a mentioned path exists or guessing where it lives. Read and write files "
|
||||
"using relative paths in the working directory; absolute paths like "
|
||||
"/mnt/data or /tmp/outputs do not exist. If the files you need are not here, "
|
||||
"ask the user to upload them or provide an exact path instead of guessing one."
|
||||
"create here; it persists across this conversation and, for a project, "
|
||||
"across the project's threads. It cannot see the user's own computer or the "
|
||||
"files stored on it. Internet access is limited: the python tool can fetch "
|
||||
"only from a fixed allowlist of public sites (such as github.com, "
|
||||
"huggingface.co, and pypi.org), not the user's own machines, private hosts, "
|
||||
"or arbitrary addresses, and the terminal blocks direct download commands "
|
||||
"like curl and wget. Documents the user attaches to the chat are retrieved "
|
||||
"separately and are not listed here. A repository, folder, or file the user "
|
||||
"refers to is not present here unless you created it here or it is already "
|
||||
"part of this project, so list the working directory to see what is "
|
||||
"available rather than assuming a mentioned path exists or guessing where it "
|
||||
"lives. Read and write files using relative paths in the working directory; "
|
||||
"absolute paths like /mnt/data or /tmp/outputs do not exist. If the files "
|
||||
"you need are not here, ask the user to provide them or an exact path "
|
||||
"instead of guessing one."
|
||||
)
|
||||
|
||||
PYTHON_TOOL = {
|
||||
|
|
@ -5849,11 +5852,12 @@ def _bash_exec(
|
|||
base = f"Blocked command(s) for safety: {', '.join(sorted(blocked))}."
|
||||
if blocked & _NETWORK_BLOCKED_COMMANDS:
|
||||
return (
|
||||
base + " This sandbox cannot reach other machines or remote "
|
||||
"hosts over the network, so files that live on another "
|
||||
"machine are not accessible from here. Do not retry with "
|
||||
"other remote-access commands; ask the user to upload the "
|
||||
"files they want you to work with."
|
||||
base + " Direct network commands are blocked in the "
|
||||
"terminal. Public files on allowlisted sites (such as "
|
||||
"github.com, huggingface.co, or pypi.org) can be downloaded "
|
||||
"from Python code instead; the user's own machine and other "
|
||||
"private or arbitrary hosts are not reachable, so for those "
|
||||
"ask the user to upload the files they want you to work with."
|
||||
)
|
||||
return base
|
||||
elif not _harden_parent_against_proc_env_leak():
|
||||
|
|
|
|||
|
|
@ -2404,14 +2404,13 @@ _TOOL_WEB_EXPANDED_TIP = (
|
|||
)
|
||||
_TOOL_CODE_TIP = (
|
||||
"Use code execution for math, calculations, data processing, or to parse "
|
||||
"and analyze information from tool results. The python and terminal tools "
|
||||
"run in a sandbox whose working directory is an isolated scratch space that "
|
||||
"may already hold files from earlier work and can reach only a fixed "
|
||||
"allowlist of public sites, not the user's own machines or private hosts, so "
|
||||
"do not assume a file, folder, or repository the user mentions is already "
|
||||
"present. List the working directory to see what is there; if what you need "
|
||||
"is not present, ask the user to upload it or give an exact path rather than "
|
||||
"running commands against a guessed one."
|
||||
"and analyze information from tool results. It runs in a sandbox whose "
|
||||
"working directory is an isolated scratch space that may already hold files "
|
||||
"from earlier work; it cannot access the user's own computer, and its "
|
||||
"internet access is limited, so do not assume a file, folder, or repository "
|
||||
"the user mentions is already present. List the working directory to see "
|
||||
"what is there; if what you need is not present, ask the user to provide it "
|
||||
"or give an exact path rather than running commands against a guessed one."
|
||||
)
|
||||
_TOOL_ARTIFACT_TIP = (
|
||||
"For HTML, CSS, or JavaScript canvas requests, call render_html once when "
|
||||
|
|
|
|||
|
|
@ -173,6 +173,25 @@ class TestToolActionNudge:
|
|||
def test_balanced_nudge_empty_without_known_tool_categories(self):
|
||||
assert _build_tool_action_nudge(tools = [], model_name = "Llama-3.1-8B-Instruct") == ""
|
||||
|
||||
def test_code_tip_does_not_name_a_disabled_sibling_code_tool(self):
|
||||
# has_code fires when either python or terminal is present, so the code tip
|
||||
# must stay tool-neutral: a request that enabled only one must never name
|
||||
# the other as an available tool (the missing-tool hallucination the
|
||||
# render_html gate already avoids).
|
||||
py_only = _build_tool_action_nudge(
|
||||
tools = [{"type": "function", "function": {"name": "python"}}],
|
||||
model_name = "Llama-3.1-8B-Instruct",
|
||||
)
|
||||
assert "Use code execution for math" in py_only
|
||||
assert "terminal" not in py_only.lower()
|
||||
|
||||
term_only = _build_tool_action_nudge(
|
||||
tools = [{"type": "function", "function": {"name": "terminal"}}],
|
||||
model_name = "Llama-3.1-8B-Instruct",
|
||||
)
|
||||
assert "Use code execution for math" in term_only
|
||||
assert "python" not in term_only.lower()
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# Pydantic model tests
|
||||
|
|
|
|||
|
|
@ -22,7 +22,12 @@ _BACKEND_DIR = str(Path(__file__).resolve().parent.parent)
|
|||
if _BACKEND_DIR not in sys.path:
|
||||
sys.path.insert(0, _BACKEND_DIR)
|
||||
|
||||
from core.inference.tools import PYTHON_TOOL, TERMINAL_TOOL, _SANDBOX_PATHS_NOTE
|
||||
from core.inference.tools import (
|
||||
PYTHON_TOOL,
|
||||
TERMINAL_TOOL,
|
||||
_SANDBOX_PATHS_NOTE,
|
||||
_bash_exec,
|
||||
)
|
||||
|
||||
|
||||
def test_note_does_not_claim_full_network_isolation():
|
||||
|
|
@ -48,3 +53,34 @@ def test_note_does_not_claim_project_sandbox_starts_empty():
|
|||
def test_note_is_appended_to_both_tool_descriptions():
|
||||
assert PYTHON_TOOL["function"]["description"].endswith(_SANDBOX_PATHS_NOTE)
|
||||
assert TERMINAL_TOOL["function"]["description"].endswith(_SANDBOX_PATHS_NOTE)
|
||||
|
||||
|
||||
def test_note_scopes_network_block_to_the_terminal_not_all_shell_commands():
|
||||
# The terminal leaves the network namespace intact and does not block git/pip,
|
||||
# so the note must not claim shell network is fully blocked; it names the
|
||||
# commands that are blocked (curl / wget) and attributes the host allowlist to
|
||||
# the python tool.
|
||||
lowered = _SANDBOX_PATHS_NOTE.lower()
|
||||
assert "shell network commands are blocked" not in lowered
|
||||
assert "curl" in lowered and "wget" in lowered
|
||||
assert "python tool can fetch" in lowered
|
||||
|
||||
|
||||
def test_note_distinguishes_attachments_from_sandbox_uploads():
|
||||
# Chat/project document uploads land in the RAG store, not the sandbox workdir,
|
||||
# so the note must not tell the model that uploaded files appear via `ls`.
|
||||
lowered = _SANDBOX_PATHS_NOTE.lower()
|
||||
assert "was uploaded" not in lowered
|
||||
assert "or that is uploaded" not in lowered
|
||||
assert "retrieved separately" in lowered
|
||||
assert "attach" in lowered
|
||||
|
||||
|
||||
def test_blocked_network_command_message_points_to_python_egress():
|
||||
# A blocked curl/wget must not tell the model the whole sandbox is offline: the
|
||||
# python tool can still fetch allowlisted public hosts.
|
||||
msg = _bash_exec("curl https://raw.githubusercontent.com/foo/bar/main/x.py").lower()
|
||||
assert "blocked command" in msg
|
||||
assert "cannot reach other machines or remote hosts" not in msg
|
||||
assert "python" in msg
|
||||
assert "github.com" in msg
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue