openreward 0.1.96.dev1__tar.gz → 0.1.96.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/PKG-INFO +1 -1
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/environments/client.py +2 -2
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/toolsets/__init__.py +6 -6
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/toolsets/hermes.py +20 -48
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/toolsets/openclaw.py +19 -32
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward.egg-info/PKG-INFO +1 -1
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/pyproject.toml +1 -1
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/tests/test_session_toolset.py +0 -136
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/README.md +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/__init__.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/_version.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/__init__.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/_session/__init__.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/_session/http.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/_session/ping.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/_session/session.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/environments/__init__.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/environments/types.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/__init__.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/background.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/rollout.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/serializers/__init__.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/serializers/ant.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/serializers/base.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/serializers/gdm.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/serializers/models.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/serializers/oai_completions.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/serializers/oai_responses.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/serializers/utils.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/sandboxes/__init__.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/sandboxes/client.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/sandboxes/secrets.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/sandboxes/types.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/cli.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/client.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/environments/__init__.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/environments/environment.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/environments/reconnect.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/environments/server.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/environments/session.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/environments/toolset.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/environments/types.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/environments/utils.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/http_client.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/log_utils.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/models.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/__init__.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/basic/Dockerfile +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/basic/__init__.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/basic/requirements.txt +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/basic/requirements.txt.tmpl +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/basic/server.py.tmpl +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/sandbox/Dockerfile +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/sandbox/__init__.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/sandbox/requirements.txt +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/sandbox/sandbox_env.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/sandbox/server.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/toolsets/claude_code.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/toolsets/codex.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/toolsets/excel.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/toolsets/pdf.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/toolsets/powerpoint.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/toolsets/word.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward.egg-info/SOURCES.txt +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward.egg-info/dependency_links.txt +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward.egg-info/entry_points.txt +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward.egg-info/requires.txt +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward.egg-info/top_level.txt +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/setup.cfg +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/tests/test_environment.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/tests/test_errors.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/tests/test_rollout_info.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/tests/test_sanitise.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/tests/test_session.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/tests/test_tool_conversion.py +0 -0
- {openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/tests/test_toolsets.py +0 -0
|
@@ -12,8 +12,8 @@ from openreward.api._session.http import (
|
|
|
12
12
|
)
|
|
13
13
|
from openreward.api._session.session import BaseAsyncSession, SessionTerminatedError
|
|
14
14
|
|
|
15
|
-
BuiltinToolset = Literal["claude-code", "codex"
|
|
16
|
-
_VALID_BUILTIN_TOOLSETS = {"claude-code", "codex"
|
|
15
|
+
BuiltinToolset = Literal["claude-code", "codex"]
|
|
16
|
+
_VALID_BUILTIN_TOOLSETS = {"claude-code", "codex"}
|
|
17
17
|
from .types import (
|
|
18
18
|
ImageBlock,
|
|
19
19
|
JSONObject,
|
|
@@ -5,8 +5,8 @@ from openreward.environments.toolset import Toolset
|
|
|
5
5
|
from .claude_code import ClaudeCodeToolset
|
|
6
6
|
from .codex import CodexToolset
|
|
7
7
|
from .excel import ExcelToolset
|
|
8
|
-
from .hermes import
|
|
9
|
-
from .openclaw import
|
|
8
|
+
from .hermes import HermesToolset
|
|
9
|
+
from .openclaw import OpenClawToolset
|
|
10
10
|
from .pdf import PDFToolset
|
|
11
11
|
from .powerpoint import PowerPointToolset
|
|
12
12
|
from .word import WordToolset
|
|
@@ -18,8 +18,8 @@ from .word import WordToolset
|
|
|
18
18
|
BUILTIN_TOOLSETS: dict[str, type[Toolset]] = {
|
|
19
19
|
ClaudeCodeToolset.name(): ClaudeCodeToolset,
|
|
20
20
|
CodexToolset.name(): CodexToolset,
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
HermesToolset.name(): HermesToolset,
|
|
22
|
+
OpenClawToolset.name(): OpenClawToolset,
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
__all__ = [
|
|
@@ -27,8 +27,8 @@ __all__ = [
|
|
|
27
27
|
"ClaudeCodeToolset",
|
|
28
28
|
"CodexToolset",
|
|
29
29
|
"ExcelToolset",
|
|
30
|
-
"
|
|
31
|
-
"
|
|
30
|
+
"HermesToolset",
|
|
31
|
+
"OpenClawToolset",
|
|
32
32
|
"PowerPointToolset",
|
|
33
33
|
"WordToolset",
|
|
34
34
|
"PDFToolset",
|
|
@@ -1,23 +1,15 @@
|
|
|
1
|
-
"""Hermes Agent
|
|
1
|
+
"""Hermes Agent session toolset.
|
|
2
2
|
|
|
3
|
-
Provides the five built-in tools
|
|
4
|
-
(``terminal``, ``read_file``, ``write_file``, ``search_files``, ``patch``)
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
isolated environment.
|
|
8
|
-
|
|
9
|
-
For the non-sandboxed variant (``hermes``), Hermes uses its own native tools
|
|
10
|
-
and only environment-specific tools (e.g. ``submit_answer``) are exposed via
|
|
11
|
-
MCP — no SDK toolset is needed.
|
|
12
|
-
|
|
13
|
-
Tool names, parameter schemas, and descriptions match Hermes Agent's upstream
|
|
14
|
-
registry definitions (``nousresearch/hermes-agent``).
|
|
3
|
+
Provides the five built-in coding tools Hermes Agent exposes
|
|
4
|
+
(``terminal``, ``read_file``, ``write_file``, ``search_files``, ``patch``).
|
|
5
|
+
Tool names, parameter schemas, and descriptions match Hermes Agent's
|
|
6
|
+
upstream registry definitions (``nousresearch/hermes-agent``).
|
|
15
7
|
"""
|
|
16
8
|
from __future__ import annotations
|
|
17
9
|
|
|
18
10
|
import base64
|
|
19
11
|
import os
|
|
20
|
-
from typing import Any,
|
|
12
|
+
from typing import Any, Optional
|
|
21
13
|
|
|
22
14
|
from pydantic import BaseModel
|
|
23
15
|
|
|
@@ -26,8 +18,6 @@ from openreward.environments.toolset import Toolset
|
|
|
26
18
|
from openreward.environments.types import TextBlock, ToolOutput
|
|
27
19
|
|
|
28
20
|
|
|
29
|
-
# ── Sandbox text helpers (inlined; same as claude_code.py) ──
|
|
30
|
-
|
|
31
21
|
async def _download_text(sandbox: Any, path: str) -> str:
|
|
32
22
|
data = await sandbox.download(path)
|
|
33
23
|
return data.decode("utf-8")
|
|
@@ -130,12 +120,14 @@ Include enough surrounding context to ensure uniqueness."""
|
|
|
130
120
|
|
|
131
121
|
# ── Toolset ──
|
|
132
122
|
|
|
133
|
-
class
|
|
134
|
-
"""
|
|
123
|
+
class HermesToolset(Toolset):
|
|
124
|
+
"""Session toolset exposing the Hermes Agent five-tool coding surface.
|
|
135
125
|
|
|
136
126
|
The toolset is bound to a session by passing it to ``env.session(...)``::
|
|
137
127
|
|
|
138
|
-
|
|
128
|
+
from openreward.toolsets import HermesToolset
|
|
129
|
+
|
|
130
|
+
with env.session(task=task, toolset="hermes") as session:
|
|
139
131
|
session.call_tool("terminal", {"command": "ls"})
|
|
140
132
|
|
|
141
133
|
Requires the bound environment to define ``self.sandbox``.
|
|
@@ -143,7 +135,7 @@ class HermesSandboxedToolset(Toolset):
|
|
|
143
135
|
|
|
144
136
|
@classmethod
|
|
145
137
|
def name(cls) -> str:
|
|
146
|
-
return "hermes
|
|
138
|
+
return "hermes"
|
|
147
139
|
|
|
148
140
|
@tool
|
|
149
141
|
async def terminal(self, params: TerminalParams) -> ToolOutput:
|
|
@@ -168,15 +160,11 @@ class HermesSandboxedToolset(Toolset):
|
|
|
168
160
|
content = await _download_text(self.sandbox, params.path)
|
|
169
161
|
lines = content.splitlines()
|
|
170
162
|
|
|
171
|
-
# Apply offset (1-indexed) and limit
|
|
172
163
|
start = max(0, params.offset - 1)
|
|
173
164
|
end = start + params.limit
|
|
174
165
|
selected_lines = lines[start:end]
|
|
175
166
|
|
|
176
|
-
|
|
177
|
-
output_lines = []
|
|
178
|
-
for i, line in enumerate(selected_lines, start=start + 1):
|
|
179
|
-
output_lines.append(f"{i}|{line}")
|
|
167
|
+
output_lines = [f"{i}|{line}" for i, line in enumerate(selected_lines, start=start + 1)]
|
|
180
168
|
output = "\n".join(output_lines)
|
|
181
169
|
|
|
182
170
|
return ToolOutput(
|
|
@@ -221,7 +209,6 @@ class HermesSandboxedToolset(Toolset):
|
|
|
221
209
|
async def search_files(self, params: SearchFilesParams) -> ToolOutput:
|
|
222
210
|
try:
|
|
223
211
|
if params.target == "files":
|
|
224
|
-
# File name search using find
|
|
225
212
|
cmd = f"find {params.path} -type f -name '{params.pattern}'"
|
|
226
213
|
output, code = await self.sandbox.run(cmd)
|
|
227
214
|
if code != 0:
|
|
@@ -230,7 +217,6 @@ class HermesSandboxedToolset(Toolset):
|
|
|
230
217
|
blocks=[TextBlock(text=f"search_files failed (exit {code}):\n{output}")],
|
|
231
218
|
finished=False,
|
|
232
219
|
)
|
|
233
|
-
# Apply offset/limit to results
|
|
234
220
|
lines = [l for l in output.splitlines() if l.strip()]
|
|
235
221
|
lines = lines[params.offset:params.offset + params.limit]
|
|
236
222
|
result = "\n".join(lines)
|
|
@@ -241,14 +227,8 @@ class HermesSandboxedToolset(Toolset):
|
|
|
241
227
|
finished=False,
|
|
242
228
|
)
|
|
243
229
|
else:
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
if params.file_glob:
|
|
247
|
-
glob_flag = f" --include='{params.file_glob}'"
|
|
248
|
-
|
|
249
|
-
context_flag = ""
|
|
250
|
-
if params.context > 0:
|
|
251
|
-
context_flag = f" -C {params.context}"
|
|
230
|
+
glob_flag = f" --include='{params.file_glob}'" if params.file_glob else ""
|
|
231
|
+
context_flag = f" -C {params.context}" if params.context > 0 else ""
|
|
252
232
|
|
|
253
233
|
if params.output_mode == "files_only":
|
|
254
234
|
mode_flag = " -l"
|
|
@@ -268,7 +248,6 @@ class HermesSandboxedToolset(Toolset):
|
|
|
268
248
|
finished=False,
|
|
269
249
|
)
|
|
270
250
|
|
|
271
|
-
# Apply offset/limit to results
|
|
272
251
|
lines = output.splitlines()
|
|
273
252
|
lines = lines[params.offset:params.offset + params.limit]
|
|
274
253
|
result = "\n".join(lines)
|
|
@@ -300,7 +279,6 @@ class HermesSandboxedToolset(Toolset):
|
|
|
300
279
|
)
|
|
301
280
|
|
|
302
281
|
async def _patch_replace(self, params: PatchParams) -> ToolOutput:
|
|
303
|
-
"""Replace mode: find a unique string and replace it."""
|
|
304
282
|
try:
|
|
305
283
|
if not params.path or params.old_string is None or params.new_string is None:
|
|
306
284
|
return ToolOutput(
|
|
@@ -346,7 +324,6 @@ class HermesSandboxedToolset(Toolset):
|
|
|
346
324
|
)
|
|
347
325
|
|
|
348
326
|
async def _patch_v4a(self, params: PatchParams) -> ToolOutput:
|
|
349
|
-
"""Patch mode: apply V4A multi-file patch content."""
|
|
350
327
|
try:
|
|
351
328
|
if not params.patch:
|
|
352
329
|
return ToolOutput(
|
|
@@ -355,14 +332,11 @@ class HermesSandboxedToolset(Toolset):
|
|
|
355
332
|
finished=False,
|
|
356
333
|
)
|
|
357
334
|
|
|
358
|
-
# Upload patch content to a temp file and apply via patch command
|
|
359
335
|
patch_tmp = "/tmp/_hermes_patch.diff"
|
|
360
336
|
await _upload_text(self.sandbox, patch_tmp, params.patch, ensure_trailing_newline=True)
|
|
361
337
|
|
|
362
|
-
# Try applying as a unified diff first
|
|
363
338
|
output, code = await self.sandbox.run(f"patch -p1 < {patch_tmp}")
|
|
364
339
|
if code != 0:
|
|
365
|
-
# Clean up and report
|
|
366
340
|
await self.sandbox.run(f"rm -f {patch_tmp}")
|
|
367
341
|
return ToolOutput(
|
|
368
342
|
metadata={"error": output, "exit_code": code},
|
|
@@ -385,10 +359,8 @@ class HermesSandboxedToolset(Toolset):
|
|
|
385
359
|
)
|
|
386
360
|
|
|
387
361
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
HermesSandboxedToolset.search_files.__doc__ = SEARCH_FILES_DESCRIPTION
|
|
394
|
-
HermesSandboxedToolset.patch.__doc__ = PATCH_DESCRIPTION
|
|
362
|
+
HermesToolset.terminal.__doc__ = TERMINAL_DESCRIPTION
|
|
363
|
+
HermesToolset.read_file.__doc__ = READ_FILE_DESCRIPTION
|
|
364
|
+
HermesToolset.write_file.__doc__ = WRITE_FILE_DESCRIPTION
|
|
365
|
+
HermesToolset.search_files.__doc__ = SEARCH_FILES_DESCRIPTION
|
|
366
|
+
HermesToolset.patch.__doc__ = PATCH_DESCRIPTION
|
|
@@ -1,17 +1,9 @@
|
|
|
1
|
-
"""OpenClaw
|
|
1
|
+
"""OpenClaw session toolset.
|
|
2
2
|
|
|
3
|
-
Provides the built-in tools
|
|
4
|
-
(``exec``, ``process``, ``read``, ``write``, ``edit``, ``apply_patch``)
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
are routed through the sandbox so the agent runs in an isolated environment.
|
|
8
|
-
|
|
9
|
-
For the non-sandboxed variant (``openclaw``), OpenClaw uses its own native
|
|
10
|
-
tools and only environment-specific tools (e.g. ``submit_answer``) are
|
|
11
|
-
exposed via MCP — no SDK toolset is needed.
|
|
12
|
-
|
|
13
|
-
Tool names, parameter schemas, and descriptions match OpenClaw's upstream
|
|
14
|
-
definitions.
|
|
3
|
+
Provides the six built-in coding tools OpenClaw exposes
|
|
4
|
+
(``exec``, ``process``, ``read``, ``write``, ``edit``, ``apply_patch``).
|
|
5
|
+
Tool names, parameter schemas, and descriptions match OpenClaw's
|
|
6
|
+
upstream definitions.
|
|
15
7
|
"""
|
|
16
8
|
from __future__ import annotations
|
|
17
9
|
|
|
@@ -19,15 +11,13 @@ import base64
|
|
|
19
11
|
import os
|
|
20
12
|
from typing import Any, List, Optional
|
|
21
13
|
|
|
22
|
-
from pydantic import BaseModel
|
|
14
|
+
from pydantic import BaseModel
|
|
23
15
|
|
|
24
16
|
from openreward.environments.environment import tool
|
|
25
17
|
from openreward.environments.toolset import Toolset
|
|
26
18
|
from openreward.environments.types import TextBlock, ToolOutput
|
|
27
19
|
|
|
28
20
|
|
|
29
|
-
# ── Sandbox text helpers (inlined; same as claude_code.py) ──
|
|
30
|
-
|
|
31
21
|
async def _download_text(sandbox: Any, path: str) -> str:
|
|
32
22
|
data = await sandbox.download(path)
|
|
33
23
|
return data.decode("utf-8")
|
|
@@ -132,12 +122,14 @@ operations: '*** Add File:', '*** Update File:' (with optional '*** Move to:'),
|
|
|
132
122
|
|
|
133
123
|
# ── Toolset ──
|
|
134
124
|
|
|
135
|
-
class
|
|
136
|
-
"""
|
|
125
|
+
class OpenClawToolset(Toolset):
|
|
126
|
+
"""Session toolset exposing the OpenClaw six-tool coding surface.
|
|
137
127
|
|
|
138
128
|
The toolset is bound to a session by passing it to ``env.session(...)``::
|
|
139
129
|
|
|
140
|
-
|
|
130
|
+
from openreward.toolsets import OpenClawToolset
|
|
131
|
+
|
|
132
|
+
with env.session(task=task, toolset="openclaw") as session:
|
|
141
133
|
session.call_tool("exec", {"command": "ls"})
|
|
142
134
|
|
|
143
135
|
Requires the bound environment to define ``self.sandbox``.
|
|
@@ -145,7 +137,7 @@ class OpenClawSandboxedToolset(Toolset):
|
|
|
145
137
|
|
|
146
138
|
@classmethod
|
|
147
139
|
def name(cls) -> str:
|
|
148
|
-
return "openclaw
|
|
140
|
+
return "openclaw"
|
|
149
141
|
|
|
150
142
|
@tool
|
|
151
143
|
async def exec(self, params: ExecParams) -> ToolOutput:
|
|
@@ -187,7 +179,6 @@ class OpenClawSandboxedToolset(Toolset):
|
|
|
187
179
|
)
|
|
188
180
|
|
|
189
181
|
if action in ("poll", "log"):
|
|
190
|
-
# Read output from a background process log file
|
|
191
182
|
tail_n = params.limit or 200
|
|
192
183
|
cmd = f"cat /tmp/_oc_proc_{sid}.log 2>/dev/null || echo 'No output available for session {sid}'"
|
|
193
184
|
if params.offset is not None:
|
|
@@ -204,7 +195,6 @@ class OpenClawSandboxedToolset(Toolset):
|
|
|
204
195
|
|
|
205
196
|
if action == "write":
|
|
206
197
|
data = params.data or ""
|
|
207
|
-
# Write data to the process's stdin via a named pipe or file
|
|
208
198
|
output, code = await self.sandbox.run(
|
|
209
199
|
f"echo '{data}' >> /tmp/_oc_proc_{sid}.stdin 2>/dev/null"
|
|
210
200
|
)
|
|
@@ -247,7 +237,7 @@ class OpenClawSandboxedToolset(Toolset):
|
|
|
247
237
|
lines = content.splitlines()
|
|
248
238
|
|
|
249
239
|
if params.offset is not None or params.limit is not None:
|
|
250
|
-
start = (params.offset or 1) - 1
|
|
240
|
+
start = (params.offset or 1) - 1
|
|
251
241
|
if params.limit is not None:
|
|
252
242
|
lines = lines[start:start + params.limit]
|
|
253
243
|
else:
|
|
@@ -331,7 +321,6 @@ class OpenClawSandboxedToolset(Toolset):
|
|
|
331
321
|
@tool
|
|
332
322
|
async def apply_patch(self, params: ApplyPatchParams) -> ToolOutput:
|
|
333
323
|
try:
|
|
334
|
-
# Upload patch content to a temp file and apply via patch command
|
|
335
324
|
patch_tmp = "/tmp/_openclaw_patch.diff"
|
|
336
325
|
await _upload_text(self.sandbox, patch_tmp, params.input, ensure_trailing_newline=True)
|
|
337
326
|
|
|
@@ -359,11 +348,9 @@ class OpenClawSandboxedToolset(Toolset):
|
|
|
359
348
|
)
|
|
360
349
|
|
|
361
350
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
OpenClawSandboxedToolset.edit.__doc__ = EDIT_DESCRIPTION
|
|
369
|
-
OpenClawSandboxedToolset.apply_patch.__doc__ = APPLY_PATCH_DESCRIPTION
|
|
351
|
+
OpenClawToolset.exec.__doc__ = EXEC_DESCRIPTION
|
|
352
|
+
OpenClawToolset.process.__doc__ = PROCESS_DESCRIPTION
|
|
353
|
+
OpenClawToolset.read.__doc__ = READ_DESCRIPTION
|
|
354
|
+
OpenClawToolset.write.__doc__ = WRITE_DESCRIPTION
|
|
355
|
+
OpenClawToolset.edit.__doc__ = EDIT_DESCRIPTION
|
|
356
|
+
OpenClawToolset.apply_patch.__doc__ = APPLY_PATCH_DESCRIPTION
|
|
@@ -283,139 +283,3 @@ async def test_session_toolset_warns_on_shadow(monkeypatch):
|
|
|
283
283
|
call_warnings = [e for e in captured if e[0] == "session_toolset_shadows_env_tool"]
|
|
284
284
|
assert len(call_warnings) == 1
|
|
285
285
|
assert call_warnings[0][1]["tool"] == "bash"
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
# ── OpenClaw toolset ──
|
|
289
|
-
|
|
290
|
-
OPENCLAW_TOOLS = {"exec", "process", "read", "write", "edit", "apply_patch"}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
@pytest.mark.asyncio
|
|
294
|
-
async def test_openclaw_toolset_tools(client: AsyncOpenReward, server: str):
|
|
295
|
-
env = client.environments.get("envwithsandbox", variant="envwithsandbox", base_url=server)
|
|
296
|
-
tasks = await env.list_tasks(split="train")
|
|
297
|
-
async with env.session(tasks[0], toolset="openclaw-sandboxed") as session:
|
|
298
|
-
tools = await session.list_tools()
|
|
299
|
-
toolset_names = {t.name for t in tools}
|
|
300
|
-
for name in OPENCLAW_TOOLS:
|
|
301
|
-
assert name in toolset_names, f"missing tool {name}"
|
|
302
|
-
# Env's submit tool is preserved.
|
|
303
|
-
assert "submit" in toolset_names
|
|
304
|
-
# exec description matches OpenClaw style.
|
|
305
|
-
exec_spec = next(t for t in tools if t.name == "exec")
|
|
306
|
-
assert exec_spec.description.startswith("Execute a shell command")
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
@pytest.mark.asyncio
|
|
310
|
-
async def test_openclaw_exec_routes_to_sandbox(client: AsyncOpenReward, server: str):
|
|
311
|
-
env = client.environments.get("envwithsandbox", variant="envwithsandbox", base_url=server)
|
|
312
|
-
tasks = await env.list_tasks(split="train")
|
|
313
|
-
async with env.session(tasks[0], toolset="openclaw-sandboxed") as session:
|
|
314
|
-
result = await session.call_tool("exec", {"command": "echo hi"})
|
|
315
|
-
assert "ran: echo hi" in result.blocks[0].text
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
@pytest.mark.asyncio
|
|
319
|
-
async def test_openclaw_write_then_read_roundtrip(client: AsyncOpenReward, server: str):
|
|
320
|
-
env = client.environments.get("envwithsandbox", variant="envwithsandbox", base_url=server)
|
|
321
|
-
tasks = await env.list_tasks(split="train")
|
|
322
|
-
async with env.session(tasks[0], toolset="openclaw-sandboxed") as session:
|
|
323
|
-
await session.call_tool("write", {"path": "/tmp/oc.txt", "content": "hello openclaw"})
|
|
324
|
-
result = await session.call_tool("read", {"path": "/tmp/oc.txt"})
|
|
325
|
-
assert "hello openclaw" in result.blocks[0].text
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
@pytest.mark.asyncio
|
|
329
|
-
async def test_openclaw_edit_with_edits_array(client: AsyncOpenReward, server: str):
|
|
330
|
-
env = client.environments.get("envwithsandbox", variant="envwithsandbox", base_url=server)
|
|
331
|
-
tasks = await env.list_tasks(split="train")
|
|
332
|
-
async with env.session(tasks[0], toolset="openclaw-sandboxed") as session:
|
|
333
|
-
await session.call_tool("write", {"path": "/tmp/oc_edit.txt", "content": "foo bar baz"})
|
|
334
|
-
result = await session.call_tool("edit", {
|
|
335
|
-
"path": "/tmp/oc_edit.txt",
|
|
336
|
-
"edits": [{"oldText": "bar", "newText": "qux"}],
|
|
337
|
-
})
|
|
338
|
-
assert "Successfully edited" in result.blocks[0].text
|
|
339
|
-
read_result = await session.call_tool("read", {"path": "/tmp/oc_edit.txt"})
|
|
340
|
-
assert "qux" in read_result.blocks[0].text
|
|
341
|
-
assert "bar" not in read_result.blocks[0].text
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
@pytest.mark.asyncio
|
|
345
|
-
async def test_openclaw_apply_patch(client: AsyncOpenReward, server: str):
|
|
346
|
-
env = client.environments.get("envwithsandbox", variant="envwithsandbox", base_url=server)
|
|
347
|
-
tasks = await env.list_tasks(split="train")
|
|
348
|
-
async with env.session(tasks[0], toolset="openclaw-sandboxed") as session:
|
|
349
|
-
result = await session.call_tool("apply_patch", {
|
|
350
|
-
"input": "*** Begin Patch\n*** Update File: /tmp/test.txt\n--- old\n+++ new\n*** End Patch",
|
|
351
|
-
})
|
|
352
|
-
# apply_patch runs patch under the hood; mock sandbox returns "ran: ..."
|
|
353
|
-
assert result.blocks[0].text is not None
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
# ── Hermes toolset ──
|
|
357
|
-
|
|
358
|
-
HERMES_TOOLS = {"terminal", "read_file", "write_file", "search_files", "patch"}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
@pytest.mark.asyncio
|
|
362
|
-
async def test_hermes_toolset_tools(client: AsyncOpenReward, server: str):
|
|
363
|
-
env = client.environments.get("envwithsandbox", variant="envwithsandbox", base_url=server)
|
|
364
|
-
tasks = await env.list_tasks(split="train")
|
|
365
|
-
async with env.session(tasks[0], toolset="hermes-sandboxed") as session:
|
|
366
|
-
tools = await session.list_tools()
|
|
367
|
-
toolset_names = {t.name for t in tools}
|
|
368
|
-
for name in HERMES_TOOLS:
|
|
369
|
-
assert name in toolset_names, f"missing tool {name}"
|
|
370
|
-
assert "submit" in toolset_names
|
|
371
|
-
terminal_spec = next(t for t in tools if t.name == "terminal")
|
|
372
|
-
assert terminal_spec.description.startswith("Execute shell commands")
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
@pytest.mark.asyncio
|
|
376
|
-
async def test_hermes_terminal_routes_to_sandbox(client: AsyncOpenReward, server: str):
|
|
377
|
-
env = client.environments.get("envwithsandbox", variant="envwithsandbox", base_url=server)
|
|
378
|
-
tasks = await env.list_tasks(split="train")
|
|
379
|
-
async with env.session(tasks[0], toolset="hermes-sandboxed") as session:
|
|
380
|
-
result = await session.call_tool("terminal", {"command": "echo hi"})
|
|
381
|
-
assert "ran: echo hi" in result.blocks[0].text
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
@pytest.mark.asyncio
|
|
385
|
-
async def test_hermes_write_then_read_roundtrip(client: AsyncOpenReward, server: str):
|
|
386
|
-
env = client.environments.get("envwithsandbox", variant="envwithsandbox", base_url=server)
|
|
387
|
-
tasks = await env.list_tasks(split="train")
|
|
388
|
-
async with env.session(tasks[0], toolset="hermes-sandboxed") as session:
|
|
389
|
-
await session.call_tool("write_file", {"path": "/tmp/hm.txt", "content": "hello hermes"})
|
|
390
|
-
result = await session.call_tool("read_file", {"path": "/tmp/hm.txt"})
|
|
391
|
-
# Hermes read_file uses LINE_NUM|CONTENT format
|
|
392
|
-
assert "hello hermes" in result.blocks[0].text
|
|
393
|
-
assert "1|" in result.blocks[0].text
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
@pytest.mark.asyncio
|
|
397
|
-
async def test_hermes_search_files_content(client: AsyncOpenReward, server: str):
|
|
398
|
-
env = client.environments.get("envwithsandbox", variant="envwithsandbox", base_url=server)
|
|
399
|
-
tasks = await env.list_tasks(split="train")
|
|
400
|
-
async with env.session(tasks[0], toolset="hermes-sandboxed") as session:
|
|
401
|
-
result = await session.call_tool("search_files", {"pattern": "hello", "path": "/tmp"})
|
|
402
|
-
# search_files runs grep under the hood; mock sandbox returns "ran: ..."
|
|
403
|
-
assert result.blocks[0].text is not None
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
@pytest.mark.asyncio
|
|
407
|
-
async def test_hermes_patch_replace_mode(client: AsyncOpenReward, server: str):
|
|
408
|
-
env = client.environments.get("envwithsandbox", variant="envwithsandbox", base_url=server)
|
|
409
|
-
tasks = await env.list_tasks(split="train")
|
|
410
|
-
async with env.session(tasks[0], toolset="hermes-sandboxed") as session:
|
|
411
|
-
await session.call_tool("write_file", {"path": "/tmp/hm_patch.txt", "content": "foo bar baz"})
|
|
412
|
-
result = await session.call_tool("patch", {
|
|
413
|
-
"mode": "replace",
|
|
414
|
-
"path": "/tmp/hm_patch.txt",
|
|
415
|
-
"old_string": "bar",
|
|
416
|
-
"new_string": "qux",
|
|
417
|
-
})
|
|
418
|
-
assert "Successfully patched" in result.blocks[0].text
|
|
419
|
-
read_result = await session.call_tool("read_file", {"path": "/tmp/hm_patch.txt"})
|
|
420
|
-
assert "qux" in read_result.blocks[0].text
|
|
421
|
-
assert "bar" not in read_result.blocks[0].text
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/serializers/__init__.py
RENAMED
|
File without changes
|
{openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/serializers/ant.py
RENAMED
|
File without changes
|
{openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/serializers/base.py
RENAMED
|
File without changes
|
{openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/serializers/gdm.py
RENAMED
|
File without changes
|
{openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/serializers/models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/api/rollouts/serializers/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/basic/requirements.txt
RENAMED
|
File without changes
|
{openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/basic/requirements.txt.tmpl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/sandbox/requirements.txt
RENAMED
|
File without changes
|
{openreward-0.1.96.dev1 → openreward-0.1.96.dev2}/openreward/templates/sandbox/sandbox_env.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|