From b29c7cb95543d7a2607745849cb6f0e80faee7f0 Mon Sep 17 00:00:00 2001 From: sami jaghouar Date: Fri, 12 Jun 2026 00:34:04 +0000 Subject: [PATCH 1/8] Strengthen IPython bash and edit prompts --- src/rlm/prompt.py | 71 ++++++++++++++++++++++++++++++++++++---- src/rlm/tools/ipython.py | 5 ++- tests/test_prompt.py | 32 ++++++++++++++++-- 3 files changed, 98 insertions(+), 10 deletions(-) diff --git a/src/rlm/prompt.py b/src/rlm/prompt.py index 8f72012..11ec7a8 100644 --- a/src/rlm/prompt.py +++ b/src/rlm/prompt.py @@ -47,9 +47,36 @@ "benchmark, or API may have its own environment and normal interface. " "Evaluate external systems through their own interface, then use IPython " "to coordinate the process and analyze what comes back.\n\n" - "When running shell commands from IPython, use `%%bash` cells. Avoid " - "`!cmd` shell escapes for project commands so shell behavior is explicit " - "and multi-line commands share one shell context.\n\n" + "When running shell commands from IPython, use `%%bash` cells. This is a " + "hard syntax rule: if an IPython tool call contains `%%bash`, the `code` " + "argument must begin with exactly `%%bash\n` as its first seven " + "characters. No leading blank line, no leading spaces, no comments, no " + "imports, and no Python statements may appear before it. If a cell starts " + "with `# note\n%%bash`, IPython parses the whole cell as Python and the " + "shell command fails with `SyntaxError`.\n\n" + "Before every IPython tool call, silently check: does `code` contain " + "`%%bash`? If yes, the first line must be exactly `%%bash`. If not, " + "rewrite the tool call before sending it.\n\n" + "Correct:\n" + "```python\n" + "%%bash\n" + "# explanation belongs here, after the magic\n" + "cd /workspace/project && pytest -q\n" + "```\n" + "Wrong:\n" + "```python\n" + "# explanation before the magic breaks IPython\n" + "%%bash\n" + "cd /workspace/project && pytest -q\n" + "```\n" + "Wrong:\n" + "```python\n" + "\n" + "%%bash\n" + "cd /workspace/project && pytest -q\n" + "```\n\n" + "Avoid `!cmd` shell escapes for project commands so shell behavior is " + "explicit and multi-line commands share one shell context.\n\n" "Important: do not install dependencies into the IPython kernel just to " "make an external project import or run there. If a project import, test, " "script, CLI, or dependency check is needed, run it through that project's " @@ -58,10 +85,38 @@ "or the active project interpreter from the repo root. Treat failures from " "that native environment as the relevant result." "\n\n" - "Use Python for reading, searching, and editing files — it gives you " - "reusable variables you can slice, filter, and act on without re-reading. " - "Always assign read/search results to named variables so you can revisit " - "them later." + "Use Python for reading and searching files — it gives you reusable " + "variables you can slice, filter, and act on without re-reading. Always " + "assign read/search results to named variables so you can revisit them " + "later." +) +EDIT_SKILL_PROMPT = ( + "For targeted modifications to existing files, you must use the " + "pre-imported `edit` skill from IPython instead of manual Python file " + "writes. Read and inspect files with normal Python, then make the change " + "with exactly " + "`await edit(path=\"relative/file.py\", old_str=\"\"\"old text\"\"\", " + "new_str=\"\"\"new text\"\"\")`. The target `old_str` must appear exactly " + "once. The supported keyword arguments are `path`, `old_str`, `new_str`, " + "and optional `cwd`; do not use `file`, `old`, `new`, line numbers, " + "`after`, or `insert`. If an edit call fails because the string is not " + "found or not unique, inspect the file and retry with a smaller exact " + "snippet before falling back. Only use normal Python file I/O for creating " + "new files or for broad generated rewrites that cannot be expressed as " + "one or more exact replacements.\n\n" + "Good targeted edit pattern:\n" + "```python\n" + "from pathlib import Path\n" + "text = Path(\"pkg/module.py\").read_text()\n" + "print(text[text.index(\"def broken\") : text.index(\"def next_func\")])\n" + "await edit(path=\"pkg/module.py\", old_str=\"\"\"exact old block\"\"\", " + "new_str=\"\"\"exact new block\"\"\")\n" + "```\n" + "Do not do this for targeted edits:\n" + "```python\n" + "Path(\"pkg/module.py\").write_text(text.replace(old, new))\n" + "open(\"pkg/module.py\", \"w\").write(new_contents)\n" + "```" ) @@ -108,6 +163,8 @@ def build_system_prompt( "Each skill is also available as a shell command by the same name: ` ...`. " "Discover its CLI usage with ` --help`." ) + if "edit" in installed_skills: + skill_lines.append(EDIT_SKILL_PROMPT) if skill_lines: parts.extend(["", *skill_lines]) diff --git a/src/rlm/tools/ipython.py b/src/rlm/tools/ipython.py index 24a1831..0ddac5b 100644 --- a/src/rlm/tools/ipython.py +++ b/src/rlm/tools/ipython.py @@ -30,7 +30,10 @@ "Use !command for shell commands (e.g. !ls -la, !cat file.py, !pip install foo). " "Use !python3 to run code with the project's own packages " "(e.g. !python3 -m pytest, !python3 -c 'import numpy'). " - "Use %%bash for multi-line shell scripts." + "Use %%bash for multi-line shell scripts. If code contains %%bash, " + "the code string must begin exactly with %%bash followed by a " + "newline. Do not put a blank line, spaces, comments, imports, or " + "Python code before %%bash." ), "parameters": { "type": "object", diff --git a/tests/test_prompt.py b/tests/test_prompt.py index 72bb761..267919d 100644 --- a/tests/test_prompt.py +++ b/tests/test_prompt.py @@ -5,6 +5,7 @@ from dataclasses import dataclass from rlm.prompt import ( + EDIT_SKILL_PROMPT, GIT_HISTORY_GUARD_PROMPT, IPYTHON_CONTROL_PROMPT, build_system_prompt, @@ -16,11 +17,15 @@ class _Tool: name: str -def _prompt(active_tools: list[_Tool]) -> str: +def _prompt( + active_tools: list[_Tool], + *, + installed_skills: list[str] | None = None, +) -> str: return build_system_prompt( "/repo", None, - [], + installed_skills or [], "/repo/.rlm/messages.jsonl", allow_recursion=False, active_tools=active_tools, @@ -57,8 +62,31 @@ def test_ipython_control_prompt_included_for_ipython_tool(): assert "long-lived notebook" in prompt assert "native runtime" in prompt assert "use `%%bash` cells" in prompt + assert "first seven characters" in prompt + assert "Before every IPython tool call" in prompt assert "do not install dependencies into the IPython kernel" in prompt + assert "Use Python for reading and searching files" in prompt + assert "Use Python for reading, searching, and editing files" not in prompt def test_ipython_control_prompt_omitted_without_ipython_tool(): assert IPYTHON_CONTROL_PROMPT not in _prompt([_Tool("bash")]) + + +def test_edit_skill_prompt_included_when_edit_is_installed(): + prompt = _prompt([_Tool("ipython")], installed_skills=["edit"]) + + assert EDIT_SKILL_PROMPT in prompt + assert 'await edit(path="relative/file.py"' in prompt + assert "pre-imported `edit` skill from IPython" in prompt + assert "`old_str` must appear exactly once" in prompt + assert "do not use `file`, `old`, `new`, line numbers" in prompt + assert "inspect the file and retry with a smaller exact snippet" in prompt + assert "Only use normal Python file I/O for creating new files" in prompt + assert "Good targeted edit pattern" in prompt + + +def test_edit_skill_prompt_omitted_without_edit_skill(): + prompt = _prompt([_Tool("ipython")], installed_skills=["search_docs"]) + + assert EDIT_SKILL_PROMPT not in prompt From f74a987db2a0081adba1abc1d7813deb99c33203 Mon Sep 17 00:00:00 2001 From: sami jaghouar Date: Fri, 12 Jun 2026 01:03:33 +0000 Subject: [PATCH 2/8] Prompt edit calls to use safe string literals --- src/rlm/prompt.py | 29 ++++++++++++++++++++++++----- tests/test_prompt.py | 4 +++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/rlm/prompt.py b/src/rlm/prompt.py index 11ec7a8..3dffed2 100644 --- a/src/rlm/prompt.py +++ b/src/rlm/prompt.py @@ -94,9 +94,9 @@ "For targeted modifications to existing files, you must use the " "pre-imported `edit` skill from IPython instead of manual Python file " "writes. Read and inspect files with normal Python, then make the change " - "with exactly " - "`await edit(path=\"relative/file.py\", old_str=\"\"\"old text\"\"\", " - "new_str=\"\"\"new text\"\"\")`. The target `old_str` must appear exactly " + "with `await edit(path=\"relative/file.py\", old_str=old, new_str=new)`, " + "where `old` and `new` are exact strings. Inline string literals are fine " + "when they are valid Python. The target `old_str` must appear exactly " "once. The supported keyword arguments are `path`, `old_str`, `new_str`, " "and optional `cwd`; do not use `file`, `old`, `new`, line numbers, " "`after`, or `insert`. If an edit call fails because the string is not " @@ -104,16 +104,35 @@ "snippet before falling back. Only use normal Python file I/O for creating " "new files or for broad generated rewrites that cannot be expressed as " "one or more exact replacements.\n\n" + "Before calling `edit`, make sure the Python string syntax is valid. Do " + "not wrap text that contains `\"\"\"` inside a `\"\"\"...\"\"\"` string; " + "that creates a SyntaxError before `edit` runs. If the target text " + "contains triple double quotes, use triple single quotes (`'''...'''`) or " + "assign `old`/`new` from inspected file slices. If the text contains both " + "triple quote styles or quoting becomes complex, build `old` and `new` as " + "variables first, then pass those variables to `await edit(...)`.\n\n" "Good targeted edit pattern:\n" "```python\n" "from pathlib import Path\n" "text = Path(\"pkg/module.py\").read_text()\n" "print(text[text.index(\"def broken\") : text.index(\"def next_func\")])\n" - "await edit(path=\"pkg/module.py\", old_str=\"\"\"exact old block\"\"\", " - "new_str=\"\"\"exact new block\"\"\")\n" + "old = '''def broken():\n" + " \"\"\"Docstring with triple double quotes.\"\"\"\n" + " return False\n" + "'''\n" + "new = '''def broken():\n" + " \"\"\"Docstring with triple double quotes.\"\"\"\n" + " return True\n" + "'''\n" + "await edit(path=\"pkg/module.py\", old_str=old, new_str=new)\n" "```\n" "Do not do this for targeted edits:\n" "```python\n" + "await edit(path=\"pkg/module.py\", old_str=\"\"\"def broken():\n" + " \"\"\"Docstring closes the outer string early.\"\"\"\n" + " return False\n" + "\"\"\", new_str=\"\"\"...\n" + "\"\"\")\n" "Path(\"pkg/module.py\").write_text(text.replace(old, new))\n" "open(\"pkg/module.py\", \"w\").write(new_contents)\n" "```" diff --git a/tests/test_prompt.py b/tests/test_prompt.py index 267919d..7b1f1da 100644 --- a/tests/test_prompt.py +++ b/tests/test_prompt.py @@ -77,12 +77,14 @@ def test_edit_skill_prompt_included_when_edit_is_installed(): prompt = _prompt([_Tool("ipython")], installed_skills=["edit"]) assert EDIT_SKILL_PROMPT in prompt - assert 'await edit(path="relative/file.py"' in prompt + assert 'await edit(path="relative/file.py", old_str=old, new_str=new)' in prompt assert "pre-imported `edit` skill from IPython" in prompt assert "`old_str` must appear exactly once" in prompt assert "do not use `file`, `old`, `new`, line numbers" in prompt assert "inspect the file and retry with a smaller exact snippet" in prompt assert "Only use normal Python file I/O for creating new files" in prompt + assert 'Do not wrap text that contains `"""`' in prompt + assert "use triple single quotes" in prompt assert "Good targeted edit pattern" in prompt From 35ad50095ae833f51f58dff90ebaab62ca050873 Mon Sep 17 00:00:00 2001 From: sami jaghouar Date: Fri, 12 Jun 2026 01:47:00 +0000 Subject: [PATCH 3/8] Warn against tool markup inside IPython code --- src/rlm/prompt.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rlm/prompt.py b/src/rlm/prompt.py index 3dffed2..42b4139 100644 --- a/src/rlm/prompt.py +++ b/src/rlm/prompt.py @@ -97,7 +97,10 @@ "with `await edit(path=\"relative/file.py\", old_str=old, new_str=new)`, " "where `old` and `new` are exact strings. Inline string literals are fine " "when they are valid Python. The target `old_str` must appear exactly " - "once. The supported keyword arguments are `path`, `old_str`, `new_str`, " + "once. Inside IPython `code`, never write native tool-call markup like " + "``, ``, or ``; use Python " + "`await edit(...)` instead. " + "The supported keyword arguments are `path`, `old_str`, `new_str`, " "and optional `cwd`; do not use `file`, `old`, `new`, line numbers, " "`after`, or `insert`. If an edit call fails because the string is not " "found or not unique, inspect the file and retry with a smaller exact " From 2b40a5c981028f21cd269fe992eec54cc3b2c72b Mon Sep 17 00:00:00 2001 From: sami jaghouar Date: Fri, 12 Jun 2026 01:49:05 +0000 Subject: [PATCH 4/8] Show concrete IPython edit example --- src/rlm/prompt.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rlm/prompt.py b/src/rlm/prompt.py index 42b4139..87069f6 100644 --- a/src/rlm/prompt.py +++ b/src/rlm/prompt.py @@ -99,7 +99,8 @@ "when they are valid Python. The target `old_str` must appear exactly " "once. Inside IPython `code`, never write native tool-call markup like " "``, ``, or ``; use Python " - "`await edit(...)` instead. " + "instead, for example: `old = \"...\"; new = \"...\"; await " + "edit(path=\"pkg/module.py\", old_str=old, new_str=new)`. " "The supported keyword arguments are `path`, `old_str`, `new_str`, " "and optional `cwd`; do not use `file`, `old`, `new`, line numbers, " "`after`, or `insert`. If an edit call fails because the string is not " From 48baef4adac646f17a4755863394ba4d7190464a Mon Sep 17 00:00:00 2001 From: sami jaghouar Date: Fri, 12 Jun 2026 02:09:39 +0000 Subject: [PATCH 5/8] Reject tool markup inside IPython code --- src/rlm/tools/ipython.py | 14 ++++++++++++++ tests/test_git_block.py | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/rlm/tools/ipython.py b/src/rlm/tools/ipython.py index 0ddac5b..d0bfc70 100644 --- a/src/rlm/tools/ipython.py +++ b/src/rlm/tools/ipython.py @@ -53,7 +53,15 @@ } _ANSI_RE = re.compile(r"\x1b\[[0-9;]*m") +_NATIVE_TOOL_MARKUP_RE = re.compile(r"<(?:tool_call|arg_key|arg_value)>") IPYTHON_TIMEOUT_MAX_SECONDS = 600 +NATIVE_TOOL_MARKUP_ERROR = ( + "Error: IPython code contains native tool-call markup such as " + ", , or . That markup is not Python. " + "Inside IPython, call skills with Python syntax, for example: " + 'old = "..."; new = "..."; await edit(path="pkg/module.py", ' + "old_str=old, new_str=new)." +) class IpythonTool: @@ -97,6 +105,12 @@ def execute(self, args: dict[str, Any], context: ToolContext) -> ToolOutcome: metric_events=metric_events, ) + if _NATIVE_TOOL_MARKUP_RE.search(code): + return ToolOutcome( + content=NATIVE_TOOL_MARKUP_ERROR, + metric_events=metric_events, + ) + blocked = find_blocked_in_ipython(code) if blocked is not None: return ToolOutcome( diff --git a/tests/test_git_block.py b/tests/test_git_block.py index 2d136b4..696ee0c 100644 --- a/tests/test_git_block.py +++ b/tests/test_git_block.py @@ -327,3 +327,17 @@ def execute(self, code, timeout): ctx.repl = StubRepl() outcome = IpythonTool().execute({"code": "print(1+1)"}, ctx) assert outcome.content == "ran: print(1+1)" + + +def test_ipython_tool_refuses_native_tool_markup(): + from rlm.tools.ipython import IpythonTool + + class StubRepl: + def execute(self, code, timeout): + raise AssertionError("markup should be rejected before REPL execution") + + ctx = _ctx() + ctx.repl = StubRepl() + outcome = IpythonTool().execute({"code": "%%bash\nls"}, ctx) + assert "native tool-call markup" in outcome.content + assert "await edit" in outcome.content From 3b6dd5229095d94319858dc36bc97e341686729a Mon Sep 17 00:00:00 2001 From: sami jaghouar Date: Fri, 12 Jun 2026 03:07:31 +0000 Subject: [PATCH 6/8] Clarify raw IPython code syntax --- src/rlm/prompt.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/rlm/prompt.py b/src/rlm/prompt.py index 87069f6..e22c0cc 100644 --- a/src/rlm/prompt.py +++ b/src/rlm/prompt.py @@ -47,6 +47,11 @@ "benchmark, or API may have its own environment and normal interface. " "Evaluate external systems through their own interface, then use IPython " "to coordinate the process and analyze what comes back.\n\n" + "The IPython `code` argument is executed literally as one code cell. It " + "must contain only raw Python/IPython syntax, not chat-template wrappers, " + "native tool-call tags, JSON fields, or prose prefixes. Wrong: " + "`%%bash\\npytest -q` or `Now inspect the file`. " + "Correct: `%%bash\\npytest -q` or `print(path.read_text())`.\n\n" "When running shell commands from IPython, use `%%bash` cells. This is a " "hard syntax rule: if an IPython tool call contains `%%bash`, the `code` " "argument must begin with exactly `%%bash\n` as its first seven " From 06d495332cd86a33084c9723e1a104f3846eb057 Mon Sep 17 00:00:00 2001 From: sami jaghouar Date: Fri, 12 Jun 2026 03:10:11 +0000 Subject: [PATCH 7/8] Remove IPython markup guard --- src/rlm/tools/ipython.py | 14 -------------- tests/test_git_block.py | 14 -------------- 2 files changed, 28 deletions(-) diff --git a/src/rlm/tools/ipython.py b/src/rlm/tools/ipython.py index d0bfc70..0ddac5b 100644 --- a/src/rlm/tools/ipython.py +++ b/src/rlm/tools/ipython.py @@ -53,15 +53,7 @@ } _ANSI_RE = re.compile(r"\x1b\[[0-9;]*m") -_NATIVE_TOOL_MARKUP_RE = re.compile(r"<(?:tool_call|arg_key|arg_value)>") IPYTHON_TIMEOUT_MAX_SECONDS = 600 -NATIVE_TOOL_MARKUP_ERROR = ( - "Error: IPython code contains native tool-call markup such as " - ", , or . That markup is not Python. " - "Inside IPython, call skills with Python syntax, for example: " - 'old = "..."; new = "..."; await edit(path="pkg/module.py", ' - "old_str=old, new_str=new)." -) class IpythonTool: @@ -105,12 +97,6 @@ def execute(self, args: dict[str, Any], context: ToolContext) -> ToolOutcome: metric_events=metric_events, ) - if _NATIVE_TOOL_MARKUP_RE.search(code): - return ToolOutcome( - content=NATIVE_TOOL_MARKUP_ERROR, - metric_events=metric_events, - ) - blocked = find_blocked_in_ipython(code) if blocked is not None: return ToolOutcome( diff --git a/tests/test_git_block.py b/tests/test_git_block.py index 696ee0c..2d136b4 100644 --- a/tests/test_git_block.py +++ b/tests/test_git_block.py @@ -327,17 +327,3 @@ def execute(self, code, timeout): ctx.repl = StubRepl() outcome = IpythonTool().execute({"code": "print(1+1)"}, ctx) assert outcome.content == "ran: print(1+1)" - - -def test_ipython_tool_refuses_native_tool_markup(): - from rlm.tools.ipython import IpythonTool - - class StubRepl: - def execute(self, code, timeout): - raise AssertionError("markup should be rejected before REPL execution") - - ctx = _ctx() - ctx.repl = StubRepl() - outcome = IpythonTool().execute({"code": "%%bash\nls"}, ctx) - assert "native tool-call markup" in outcome.content - assert "await edit" in outcome.content From e660ada895920c4c75fd06759918201da3a81f2f Mon Sep 17 00:00:00 2001 From: sami jaghouar Date: Fri, 12 Jun 2026 03:57:45 +0000 Subject: [PATCH 8/8] Format IPython prompt changes --- src/rlm/prompt.py | 30 +++++++++++++++--------------- src/rlm/tools/ipython.py | 5 ++--- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/src/rlm/prompt.py b/src/rlm/prompt.py index e22c0cc..ab4ddef 100644 --- a/src/rlm/prompt.py +++ b/src/rlm/prompt.py @@ -99,13 +99,13 @@ "For targeted modifications to existing files, you must use the " "pre-imported `edit` skill from IPython instead of manual Python file " "writes. Read and inspect files with normal Python, then make the change " - "with `await edit(path=\"relative/file.py\", old_str=old, new_str=new)`, " + 'with `await edit(path="relative/file.py", old_str=old, new_str=new)`, ' "where `old` and `new` are exact strings. Inline string literals are fine " "when they are valid Python. The target `old_str` must appear exactly " "once. Inside IPython `code`, never write native tool-call markup like " "``, ``, or ``; use Python " - "instead, for example: `old = \"...\"; new = \"...\"; await " - "edit(path=\"pkg/module.py\", old_str=old, new_str=new)`. " + 'instead, for example: `old = "..."; new = "..."; await ' + 'edit(path="pkg/module.py", old_str=old, new_str=new)`. ' "The supported keyword arguments are `path`, `old_str`, `new_str`, " "and optional `cwd`; do not use `file`, `old`, `new`, line numbers, " "`after`, or `insert`. If an edit call fails because the string is not " @@ -114,7 +114,7 @@ "new files or for broad generated rewrites that cannot be expressed as " "one or more exact replacements.\n\n" "Before calling `edit`, make sure the Python string syntax is valid. Do " - "not wrap text that contains `\"\"\"` inside a `\"\"\"...\"\"\"` string; " + 'not wrap text that contains `"""` inside a `"""..."""` string; ' "that creates a SyntaxError before `edit` runs. If the target text " "contains triple double quotes, use triple single quotes (`'''...'''`) or " "assign `old`/`new` from inspected file slices. If the text contains both " @@ -123,27 +123,27 @@ "Good targeted edit pattern:\n" "```python\n" "from pathlib import Path\n" - "text = Path(\"pkg/module.py\").read_text()\n" - "print(text[text.index(\"def broken\") : text.index(\"def next_func\")])\n" + 'text = Path("pkg/module.py").read_text()\n' + 'print(text[text.index("def broken") : text.index("def next_func")])\n' "old = '''def broken():\n" - " \"\"\"Docstring with triple double quotes.\"\"\"\n" + ' """Docstring with triple double quotes."""\n' " return False\n" "'''\n" "new = '''def broken():\n" - " \"\"\"Docstring with triple double quotes.\"\"\"\n" + ' """Docstring with triple double quotes."""\n' " return True\n" "'''\n" - "await edit(path=\"pkg/module.py\", old_str=old, new_str=new)\n" + 'await edit(path="pkg/module.py", old_str=old, new_str=new)\n' "```\n" "Do not do this for targeted edits:\n" "```python\n" - "await edit(path=\"pkg/module.py\", old_str=\"\"\"def broken():\n" - " \"\"\"Docstring closes the outer string early.\"\"\"\n" + 'await edit(path="pkg/module.py", old_str="""def broken():\n' + ' """Docstring closes the outer string early."""\n' " return False\n" - "\"\"\", new_str=\"\"\"...\n" - "\"\"\")\n" - "Path(\"pkg/module.py\").write_text(text.replace(old, new))\n" - "open(\"pkg/module.py\", \"w\").write(new_contents)\n" + '""", new_str="""...\n' + '""")\n' + 'Path("pkg/module.py").write_text(text.replace(old, new))\n' + 'open("pkg/module.py", "w").write(new_contents)\n' "```" ) diff --git a/src/rlm/tools/ipython.py b/src/rlm/tools/ipython.py index 0ddac5b..babd5a3 100644 --- a/src/rlm/tools/ipython.py +++ b/src/rlm/tools/ipython.py @@ -4,11 +4,11 @@ import copy import os -from queue import Empty import re import sys import threading import time +from queue import Empty from typing import TYPE_CHECKING, Any from rlm.tools.base import ToolContext, ToolOutcome @@ -68,8 +68,7 @@ def schema(self) -> dict[str, Any]: ) schema = copy.deepcopy(IPYTHON_SCHEMA) schema["function"]["parameters"]["properties"]["timeout"]["description"] = ( - "Optional timeout in seconds. " - f"Default: {timeout}s. Maximum: {IPYTHON_TIMEOUT_MAX_SECONDS}s." + f"Optional timeout in seconds. Default: {timeout}s. Maximum: {IPYTHON_TIMEOUT_MAX_SECONDS}s." ) return schema