vanishingradient commited on
Commit
59a0ac5
·
verified ·
1 Parent(s): 681342c

ls fallback: no assume src/

Browse files
Files changed (1) hide show
  1. src/deepagents_rl/train/swe_env.py +73 -14
src/deepagents_rl/train/swe_env.py CHANGED
@@ -392,8 +392,9 @@ class DeepAgentSWEEnv:
392
  # TRL appends this string to the last user message — force tool use.
393
  return (
394
  "\n\nCRITICAL: Use structured tool/function calls only. "
395
- "Your first action MUST be a tool call to `ls` or `glob` on the repo. "
396
- "Then `read_file`/`grep`, then `edit_file`/`write_file` to fix the issue. "
 
397
  "Do not answer with a text-only plan. When the fix is applied, stop tools and say DONE."
398
  )
399
 
@@ -409,17 +410,49 @@ class DeepAgentSWEEnv:
409
  raise RuntimeError("DeepAgentSWEEnv tool called before reset()")
410
  return self._backend
411
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  @_traced_tool
413
  def ls(self, path: str = ".") -> str:
414
- """List files in a directory inside the sandbox.
415
 
416
  Args:
417
- path: Directory path relative to the repo root.
 
418
 
419
  Returns:
420
- Directory listing as text.
421
  """
422
- return _result_to_str(self._require_backend().ls(path))
 
 
 
 
 
 
 
 
 
 
 
423
 
424
  @_traced_tool
425
  def glob(self, pattern: str, path: str = ".") -> str:
@@ -427,12 +460,22 @@ class DeepAgentSWEEnv:
427
 
428
  Args:
429
  pattern: Glob pattern (e.g. '**/*.py').
430
- path: Root path for the search.
431
 
432
  Returns:
433
  Matching paths as text.
434
  """
435
- return _result_to_str(self._require_backend().glob(pattern, path))
 
 
 
 
 
 
 
 
 
 
436
 
437
  @_traced_tool
438
  def grep(self, pattern: str, path: str = ".") -> str:
@@ -440,28 +483,44 @@ class DeepAgentSWEEnv:
440
 
441
  Args:
442
  pattern: Regex pattern to search for.
443
- path: Root path for the search.
444
 
445
  Returns:
446
  Matching lines as text.
447
  """
448
- return _result_to_str(self._require_backend().grep(pattern, path))
 
 
 
 
 
 
 
 
 
449
 
450
  @_traced_tool
451
  def read_file(self, file_path: str, offset: int = 0, limit: int = 200) -> str:
452
  """Read a text file from the sandbox.
453
 
454
  Args:
455
- file_path: Path relative to the repo root.
456
  offset: Starting line offset.
457
  limit: Max number of lines to return.
458
 
459
  Returns:
460
  File contents as text.
461
  """
462
- return _result_to_str(
463
- self._require_backend().read(file_path, offset=offset, limit=limit)
464
- )
 
 
 
 
 
 
 
465
 
466
  @_traced_tool
467
  def write_file(self, file_path: str, content: str) -> str:
 
392
  # TRL appends this string to the last user message — force tool use.
393
  return (
394
  "\n\nCRITICAL: Use structured tool/function calls only. "
395
+ "Your first action MUST be: ls with path='.' (repo root). "
396
+ "Do NOT call ls on 'src' — that folder often does not exist. "
397
+ "Then read_file/grep using paths from that listing, then edit_file/write_file. "
398
  "Do not answer with a text-only plan. When the fix is applied, stop tools and say DONE."
399
  )
400
 
 
410
  raise RuntimeError("DeepAgentSWEEnv tool called before reset()")
411
  return self._backend
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():
426
+ return True
427
+ return "path_not_found" in (text or "").lower()
428
+
429
+ def _root_listing(self) -> str:
430
+ backend = self._require_backend()
431
+ return _result_to_str(backend.ls("."))
432
+
433
  @_traced_tool
434
  def ls(self, path: str = ".") -> str:
435
+ """List files in a directory inside the sandbox repo root.
436
 
437
  Args:
438
+ path: Directory relative to the repo root. Use "." for the root.
439
+ Do NOT assume a "src/" folder exists — many repos have none.
440
 
441
  Returns:
442
+ Directory listing as text (falls back to repo root if path missing).
443
  """
444
+ backend = self._require_backend()
445
+ path = self._norm_path(path)
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"Path '{path}' was not found in this repo sandbox.\n"
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
 
457
  @_traced_tool
458
  def glob(self, pattern: str, path: str = ".") -> str:
 
460
 
461
  Args:
462
  pattern: Glob pattern (e.g. '**/*.py').
463
+ path: Root path for the search (use '.' for repo root).
464
 
465
  Returns:
466
  Matching paths as text.
467
  """
468
+ backend = self._require_backend()
469
+ path = self._norm_path(path)
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. Retry with path='.'.\n"
476
+ f"Repo root listing:\n{root}"
477
+ )
478
+ return out
479
 
480
  @_traced_tool
481
  def grep(self, pattern: str, path: str = ".") -> str:
 
483
 
484
  Args:
485
  pattern: Regex pattern to search for.
486
+ path: Root path for the search (use '.' for repo root).
487
 
488
  Returns:
489
  Matching lines as text.
490
  """
491
+ backend = self._require_backend()
492
+ path = self._norm_path(path)
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. Retry with path='.' (repo root).\n"
498
+ f"Repo root listing:\n{self._root_listing()}"
499
+ )
500
+ return out
501
 
502
  @_traced_tool
503
  def read_file(self, file_path: str, offset: int = 0, limit: int = 200) -> str:
504
  """Read a text file from the sandbox.
505
 
506
  Args:
507
+ file_path: Path relative to the repo root (discover via ls/glob first).
508
  offset: Starting line offset.
509
  limit: Max number of lines to return.
510
 
511
  Returns:
512
  File contents as text.
513
  """
514
+ backend = self._require_backend()
515
+ file_path = self._norm_path(file_path)
516
+ result = backend.read(file_path, offset=offset, limit=limit)
517
+ out = _result_to_str(result)
518
+ if self._is_missing(result, out):
519
+ return (
520
+ f"File '{file_path}' not found. Use ls path='.' or glob to discover real paths.\n"
521
+ f"Repo root listing:\n{self._root_listing()}"
522
+ )
523
+ return out
524
 
525
  @_traced_tool
526
  def write_file(self, file_path: str, content: str) -> str: