Add deepagents_rl package sources
Browse files
src/deepagents_rl/train/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (389 Bytes). View file
|
|
|
src/deepagents_rl/train/__pycache__/resolve_model.cpython-312.pyc
ADDED
|
Binary file (4.04 kB). View file
|
|
|
src/deepagents_rl/train/__pycache__/swe_env.cpython-312.pyc
CHANGED
|
Binary files a/src/deepagents_rl/train/__pycache__/swe_env.cpython-312.pyc and b/src/deepagents_rl/train/__pycache__/swe_env.cpython-312.pyc differ
|
|
|
src/deepagents_rl/train/swe_env.py
CHANGED
|
@@ -44,6 +44,22 @@ def _result_to_str(obj: Any) -> str:
|
|
| 44 |
return obj[:8000]
|
| 45 |
if hasattr(obj, "output") and isinstance(obj.output, str):
|
| 46 |
return obj.output[:8000]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
if hasattr(obj, "error") and obj.error:
|
| 48 |
return f"ERROR: {obj.error}"[:8000]
|
| 49 |
try:
|
|
@@ -52,6 +68,21 @@ def _result_to_str(obj: Any) -> str:
|
|
| 52 |
return str(obj)[:8000]
|
| 53 |
|
| 54 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
def _as_str_list(val: Any) -> list[str]:
|
| 56 |
if val is None:
|
| 57 |
return []
|
|
@@ -412,14 +443,27 @@ class DeepAgentSWEEnv:
|
|
| 412 |
|
| 413 |
@staticmethod
|
| 414 |
def _norm_path(path: str | None) -> str:
|
| 415 |
-
p = (path or ".").strip()
|
| 416 |
-
if p in ("", "/", "./", ".\\"):
|
| 417 |
return "."
|
| 418 |
# virtual_mode often wants leading slash for absolute-in-sandbox paths
|
| 419 |
if p.startswith("./"):
|
| 420 |
p = p[2:]
|
|
|
|
|
|
|
|
|
|
|
|
|
| 421 |
return p or "."
|
| 422 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 423 |
def _is_missing(self, result_obj: Any, text: str) -> bool:
|
| 424 |
err = getattr(result_obj, "error", None)
|
| 425 |
if err and "path_not_found" in str(err).lower():
|
|
@@ -428,7 +472,13 @@ class DeepAgentSWEEnv:
|
|
| 428 |
|
| 429 |
def _root_listing(self) -> str:
|
| 430 |
backend = self._require_backend()
|
| 431 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 432 |
|
| 433 |
@_traced_tool
|
| 434 |
def ls(self, path: str = ".") -> str:
|
|
@@ -442,15 +492,20 @@ class DeepAgentSWEEnv:
|
|
| 442 |
Directory listing as text (falls back to repo root if path missing).
|
| 443 |
"""
|
| 444 |
backend = self._require_backend()
|
| 445 |
-
path = self.
|
| 446 |
result = backend.ls(path)
|
| 447 |
out = _result_to_str(result)
|
| 448 |
if self._is_missing(result, out):
|
| 449 |
root = self._root_listing()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 450 |
return (
|
| 451 |
-
f"
|
| 452 |
-
f"Tip: there is often no 'src/' directory. Start with path='.' (repo root).\n"
|
| 453 |
-
f"Repo root listing:\n{root}"
|
| 454 |
)
|
| 455 |
return out
|
| 456 |
|
|
@@ -466,14 +521,13 @@ class DeepAgentSWEEnv:
|
|
| 466 |
Matching paths as text.
|
| 467 |
"""
|
| 468 |
backend = self._require_backend()
|
| 469 |
-
path = self.
|
| 470 |
result = backend.glob(pattern, path)
|
| 471 |
out = _result_to_str(result)
|
| 472 |
if self._is_missing(result, out):
|
| 473 |
-
root = self._root_listing()
|
| 474 |
return (
|
| 475 |
-
f"Path '{path}' not found for glob
|
| 476 |
-
f"
|
| 477 |
)
|
| 478 |
return out
|
| 479 |
|
|
@@ -489,13 +543,13 @@ class DeepAgentSWEEnv:
|
|
| 489 |
Matching lines as text.
|
| 490 |
"""
|
| 491 |
backend = self._require_backend()
|
| 492 |
-
path = self.
|
| 493 |
result = backend.grep(pattern, path)
|
| 494 |
out = _result_to_str(result)
|
| 495 |
if self._is_missing(result, out):
|
| 496 |
return (
|
| 497 |
-
f"Path '{path}' not found for grep
|
| 498 |
-
f"
|
| 499 |
)
|
| 500 |
return out
|
| 501 |
|
|
@@ -513,14 +567,24 @@ class DeepAgentSWEEnv:
|
|
| 513 |
"""
|
| 514 |
backend = self._require_backend()
|
| 515 |
file_path = self._norm_path(file_path)
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 524 |
|
| 525 |
@_traced_tool
|
| 526 |
def write_file(self, file_path: str, content: str) -> str:
|
|
|
|
| 44 |
return obj[:8000]
|
| 45 |
if hasattr(obj, "output") and isinstance(obj.output, str):
|
| 46 |
return obj.output[:8000]
|
| 47 |
+
# Prefer listing entries before error (LsResult / GlobResult)
|
| 48 |
+
entries = getattr(obj, "entries", None)
|
| 49 |
+
if entries is not None:
|
| 50 |
+
lines: list[str] = []
|
| 51 |
+
for e in entries:
|
| 52 |
+
if isinstance(e, dict):
|
| 53 |
+
lines.append(str(e.get("path") or e))
|
| 54 |
+
else:
|
| 55 |
+
lines.append(str(e))
|
| 56 |
+
return ("\n".join(lines) if lines else "(empty directory)")[:8000]
|
| 57 |
+
matches = getattr(obj, "matches", None)
|
| 58 |
+
if matches is not None and not getattr(obj, "error", None):
|
| 59 |
+
try:
|
| 60 |
+
return json.dumps(matches, default=str)[:8000]
|
| 61 |
+
except Exception:
|
| 62 |
+
return str(matches)[:8000]
|
| 63 |
if hasattr(obj, "error") and obj.error:
|
| 64 |
return f"ERROR: {obj.error}"[:8000]
|
| 65 |
try:
|
|
|
|
| 68 |
return str(obj)[:8000]
|
| 69 |
|
| 70 |
|
| 71 |
+
# Common dirs Qwen invents that most SWE-bench repos do not have.
|
| 72 |
+
_PHANTOM_DIRS = frozenset(
|
| 73 |
+
{
|
| 74 |
+
"src",
|
| 75 |
+
"lib",
|
| 76 |
+
"app",
|
| 77 |
+
"source",
|
| 78 |
+
"sources",
|
| 79 |
+
"code",
|
| 80 |
+
"project",
|
| 81 |
+
"workspace",
|
| 82 |
+
}
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
|
| 86 |
def _as_str_list(val: Any) -> list[str]:
|
| 87 |
if val is None:
|
| 88 |
return []
|
|
|
|
| 443 |
|
| 444 |
@staticmethod
|
| 445 |
def _norm_path(path: str | None) -> str:
|
| 446 |
+
p = (path or ".").strip().strip("'\"")
|
| 447 |
+
if p in ("", "/", "./", ".\\", "~"):
|
| 448 |
return "."
|
| 449 |
# virtual_mode often wants leading slash for absolute-in-sandbox paths
|
| 450 |
if p.startswith("./"):
|
| 451 |
p = p[2:]
|
| 452 |
+
if p.startswith("/"):
|
| 453 |
+
p = p.lstrip("/") or "."
|
| 454 |
+
# Drop trailing slash for alias matching
|
| 455 |
+
p = p.rstrip("/") or "."
|
| 456 |
return p or "."
|
| 457 |
|
| 458 |
+
@classmethod
|
| 459 |
+
def _resolve_ls_path(cls, path: str | None) -> tuple[str, str | None]:
|
| 460 |
+
"""Map invented dirs (src/lib/…) to repo root before hitting the backend."""
|
| 461 |
+
p = cls._norm_path(path)
|
| 462 |
+
head = p.split("/", 1)[0].lower()
|
| 463 |
+
if head in _PHANTOM_DIRS:
|
| 464 |
+
return ".", p
|
| 465 |
+
return p, None
|
| 466 |
+
|
| 467 |
def _is_missing(self, result_obj: Any, text: str) -> bool:
|
| 468 |
err = getattr(result_obj, "error", None)
|
| 469 |
if err and "path_not_found" in str(err).lower():
|
|
|
|
| 472 |
|
| 473 |
def _root_listing(self) -> str:
|
| 474 |
backend = self._require_backend()
|
| 475 |
+
# Prefer "/" then "." for virtual_mode backends
|
| 476 |
+
for candidate in ("/", "."):
|
| 477 |
+
result = backend.ls(candidate)
|
| 478 |
+
out = _result_to_str(result)
|
| 479 |
+
if not self._is_missing(result, out):
|
| 480 |
+
return out
|
| 481 |
+
return "(unable to list repo root)"
|
| 482 |
|
| 483 |
@_traced_tool
|
| 484 |
def ls(self, path: str = ".") -> str:
|
|
|
|
| 492 |
Directory listing as text (falls back to repo root if path missing).
|
| 493 |
"""
|
| 494 |
backend = self._require_backend()
|
| 495 |
+
path, aliased_from = self._resolve_ls_path(path)
|
| 496 |
result = backend.ls(path)
|
| 497 |
out = _result_to_str(result)
|
| 498 |
if self._is_missing(result, out):
|
| 499 |
root = self._root_listing()
|
| 500 |
+
_vlog("tool.ls.fallback_root", requested=aliased_from or path)
|
| 501 |
+
return (
|
| 502 |
+
f"Path '{aliased_from or path}' does not exist here.\n"
|
| 503 |
+
f"Listing repo root instead (use these real paths):\n{root}"
|
| 504 |
+
)
|
| 505 |
+
if aliased_from and aliased_from != path:
|
| 506 |
+
_vlog("tool.ls.alias", requested=aliased_from, used=path)
|
| 507 |
return (
|
| 508 |
+
f"Note: '{aliased_from}' is not used in this repo; showing repo root:\n{out}"
|
|
|
|
|
|
|
| 509 |
)
|
| 510 |
return out
|
| 511 |
|
|
|
|
| 521 |
Matching paths as text.
|
| 522 |
"""
|
| 523 |
backend = self._require_backend()
|
| 524 |
+
path, aliased_from = self._resolve_ls_path(path)
|
| 525 |
result = backend.glob(pattern, path)
|
| 526 |
out = _result_to_str(result)
|
| 527 |
if self._is_missing(result, out):
|
|
|
|
| 528 |
return (
|
| 529 |
+
f"Path '{aliased_from or path}' not found for glob; retrying at repo root.\n"
|
| 530 |
+
f"{_result_to_str(backend.glob(pattern, '.'))}"
|
| 531 |
)
|
| 532 |
return out
|
| 533 |
|
|
|
|
| 543 |
Matching lines as text.
|
| 544 |
"""
|
| 545 |
backend = self._require_backend()
|
| 546 |
+
path, aliased_from = self._resolve_ls_path(path)
|
| 547 |
result = backend.grep(pattern, path)
|
| 548 |
out = _result_to_str(result)
|
| 549 |
if self._is_missing(result, out):
|
| 550 |
return (
|
| 551 |
+
f"Path '{aliased_from or path}' not found for grep; retrying at repo root.\n"
|
| 552 |
+
f"{_result_to_str(backend.grep(pattern, '.'))}"
|
| 553 |
)
|
| 554 |
return out
|
| 555 |
|
|
|
|
| 567 |
"""
|
| 568 |
backend = self._require_backend()
|
| 569 |
file_path = self._norm_path(file_path)
|
| 570 |
+
# If model invents src/foo.py, try stripping the phantom prefix once.
|
| 571 |
+
head = file_path.split("/", 1)[0].lower()
|
| 572 |
+
candidates = [file_path]
|
| 573 |
+
if head in _PHANTOM_DIRS and "/" in file_path:
|
| 574 |
+
candidates.append(file_path.split("/", 1)[1])
|
| 575 |
+
last_out = ""
|
| 576 |
+
for cand in candidates:
|
| 577 |
+
result = backend.read(cand, offset=offset, limit=limit)
|
| 578 |
+
out = _result_to_str(result)
|
| 579 |
+
last_out = out
|
| 580 |
+
if not self._is_missing(result, out):
|
| 581 |
+
if cand != file_path:
|
| 582 |
+
return f"(read via '{cand}' — '{file_path}' missing)\n{out}"
|
| 583 |
+
return out
|
| 584 |
+
return (
|
| 585 |
+
f"File '{file_path}' not found. Use ls path='.' or glob to discover real paths.\n"
|
| 586 |
+
f"Repo root listing:\n{self._root_listing()}\nLast error: {last_out}"
|
| 587 |
+
)
|
| 588 |
|
| 589 |
@_traced_tool
|
| 590 |
def write_file(self, file_path: str, content: str) -> str:
|