Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ The project uses a `src/smolagents/` layout. All source code is under `src/smola
- `prompts/` - YAML prompt templates for different agent types

### Beyond Python (BP) Extensions
- `bp_cli.py` - Interactive CLI (`bpsa`): REPL and one-shot modes with CodeAgent, slash commands, token tracking
- `bp_cli.py` - Interactive CLI (`bpsa`): REPL and one-shot modes with CodeAgent, slash commands, token tracking, shell escapes (`!`, `!!`, `!!!`), `/alias`, `/redo`, auto-save
- `bp_tools.py` - Extended tool library: file I/O, source code analysis, OS commands, multi-language support (24+ languages including Pascal, PHP, C++, Java, Go, Rust)
- `bp_thinkers.py` - `Thinker` agent with multi-step reasoning (thoughts/plans/code sections)
- `bp_executors.py` - `LocalExecExecutor` for direct Python execution via `exec()`
Expand Down
18 changes: 14 additions & 4 deletions CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ Use `prompt_toolkit` for:
- Ctrl+C handling (cancel current input, not exit)
- Autocomplete (slash commands)

### Shell Escapes

| Command | Description |
|---------|-------------|
| `!<command>` | Run an OS command directly (agent does not see the output) |
| `!!<command>` | Run an OS command; output is appended to the next prompt sent to the agent |
| `!!!<command>` | Run an OS command and immediately send the output to the agent for analysis |

### Step Display

- Show intermediate code/thoughts as they happen (streaming)
Expand All @@ -126,12 +134,14 @@ Use `prompt_toolkit` for:
| `/compression-max-uncompressed-steps <N>` | Change max_uncompressed_steps |
| `/compression-model <model>` | Switch compression model |
| `/exit` | Exit the REPL |
| `/file <path>` | Load a file's content as the prompt |
| `/help` | Show available commands and brief descriptions |
| `/load-instructions` | Load agent instruction files into next prompt |
| `/plan [on\|off\|N]` | Toggle or set planning interval (default: 22) |
| `/pwd` | Show current working directory |
| `/run <script.py>` | Execute a Python script |
| `/repeat <N> <prompt>` | Run the same prompt N times, each on a fresh agent with current context |
| `/repeat-prompt <N> <path>` | Run a prompt file N times, each on a fresh agent with current context |
| `/run-prompt <path>` | Load a file's content as the prompt |
| `/run-py <script.py>` | Execute a Python script |
| `/save <filename>` | Save the last answer to a file |
| `/save-step <N> <file>` | Save full content of step N to a file |
| `/session-load <file>` | Load a session from a JSON file |
Expand All @@ -141,8 +151,8 @@ Use `prompt_toolkit` for:
| `/show-stats` | Show session statistics (token usage, time) |
| `/show-step <N>` | Show full content of a specific step |
| `/show-steps` | Show one-line summary of all memory steps |
| `/steps <N>` | Change max_steps for the agent |
| `/tools` | List all loaded tools |
| `/set-max-steps <N>` | Change max_steps for the agent |
| `/show-tools` | List all loaded tools |
| `/undo-steps [N]` | Remove last N steps from memory (default: 1) |
| `/verbose` | Toggle verbose output |

Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,23 @@ $ bpsa --load-instructions # Load CLAUDE.md, AGENTS.md, etc. at startup
$ bpsa --browser # Enable Playwright browser integration
```

The REPL supports command history, tab completion for slash commands, and multi-line input via Alt+Enter. Use `/session-save <file>` and `/session-load <file>` to persist and restore sessions across restarts.
The REPL supports command history, tab completion for slash commands, and multi-line input via Alt+Enter. Use `/session-save <file>` and `/session-load <file>` to persist and restore sessions across restarts. You can also launch `ad-infinitum` from within the REPL via `!ad-infinitum ...`. Type `/help` to see all available commands.

#### Shell commands from the REPL

| Prefix | Description |
|--------|-------------|
| `!<command>` | Run an OS command directly (agent does not see the output) |
| `!!<command>` | Run an OS command with streaming output; output is appended to the next prompt sent to the agent |
| `!!!<command>` | Run an OS command and immediately send the output to the agent for analysis |

#### Aliases

Define command aliases with `/alias <name> <value>` (e.g., `/alias gs !!git status`). Aliases are saved to `~/.bpsa_aliases` and persist across sessions. Use `/alias` to list all and `/alias -d <name>` to delete.

#### Auto-save

Sessions are automatically saved every 5 turns to `~/.bpsa_autosave.json`. Configure the interval with the `BPSA_AUTOSAVE_INTERVAL` environment variable (set to 0 to disable).


## CLI (`ad-infinitum`)
Expand Down
14 changes: 10 additions & 4 deletions src/smolagents/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -687,19 +687,23 @@ def _finalize_step(self, memory_step: ActionStep | PlanningStep):

def _handle_max_steps_reached(self, task: str) -> Any:
action_step_start_time = time.time()
final_answer = self.provide_final_answer(task)
# BP: replaced forced LLM final answer with a static message to save tokens
# final_answer = self.provide_final_answer(task)
message = f"Max steps reached ({self.max_steps}/{self.max_steps}) - should I continue?"
final_memory_step = ActionStep(
step_number=self.step_number,
error=AgentMaxStepsError("Reached max steps.", self.logger),
timing=Timing(start_time=action_step_start_time, end_time=time.time()),
token_usage=final_answer.token_usage,
# token_usage=final_answer.token_usage,
actionstep_id=self._next_actionstep_id,
)
self._next_actionstep_id += 1
final_memory_step.action_output = final_answer.content
# final_memory_step.action_output = final_answer.content
final_memory_step.action_output = message
self._finalize_step(final_memory_step)
self.memory.steps.append(final_memory_step)
return final_answer.content
# return final_answer.content
return message

def _generate_planning_step(
self, task, is_first_step: bool, step: int
Expand Down Expand Up @@ -1431,6 +1435,7 @@ def initialize_system_prompt(self) -> str:
"tools": self.tools,
"managed_agents": self.managed_agents,
"custom_instructions": self.instructions,
"model_id": getattr(self.model, 'model_id', 'unknown') or 'unknown',
},
)
return system_prompt
Expand Down Expand Up @@ -1810,6 +1815,7 @@ def initialize_system_prompt(self) -> str:
"custom_instructions": self.instructions,
"code_block_opening_tag": self.code_block_tags[0],
"code_block_closing_tag": self.code_block_tags[1],
"model_id": getattr(self.model, 'model_id', 'unknown') or 'unknown',
},
)
return system_prompt
Expand Down
21 changes: 5 additions & 16 deletions src/smolagents/bp_ad_infinitum.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
BPSA_PLAN_INTERVAL - Planning interval (default: None = off)
BPSA_MAX_STEPS - Max steps per agent run (default: 200)
BPSA_COOLDOWN - Seconds to wait between cycles (default: 0)
BPSA_INJECT_FOLDER - Inject directory tree (default: false, true = cwd, or a path)
BPSA_INJECT_FOLDER - Inject directory tree (default: true = cwd, false = off, or a path)
"""

import glob
Expand Down Expand Up @@ -177,19 +177,8 @@ def get_int_env(name: str, default: int) -> int:

def inject_tree(folder: str) -> str:
"""Generate directory tree string to append to task prompts."""
from smolagents.bp_tools import list_directory_tree
tree = list_directory_tree(folder_path=folder, add_function_signatures=True)
return (
"\nThis is the result of list_directory_tree:\n<directory_tree>\n"
+ tree
+ "\n</directory_tree>\n"
"The contents of <directory_tree></directory_tree> is VERY important to you. "
"From <directory_tree></directory_tree>, you can get a general view/current state of the project:\n"
"* From the md files, if they exist, you can find the existing section titles "
"and have a general idea of the md file contents.\n"
"* For source code files, if they exist, you can find class and method names "
"so you can also develop a general idea of their contents.\n"
)
from smolagents.bp_tools import inject_tree as _inject_tree
return _inject_tree(folder)


def run_script(task: TaskItem) -> subprocess.CompletedProcess:
Expand Down Expand Up @@ -368,9 +357,9 @@ def main():
max_steps = get_int_env("BPSA_MAX_STEPS", 200)
cooldown = get_int_env("BPSA_COOLDOWN", 0)
tree_folder_raw = get_env("BPSA_INJECT_FOLDER")
if tree_folder_raw is None or tree_folder_raw.lower() == "false":
if tree_folder_raw is not None and tree_folder_raw.lower() == "false":
tree_folder = None
elif tree_folder_raw.lower() == "true":
elif tree_folder_raw is None or tree_folder_raw.lower() == "true":
tree_folder = os.getcwd()
else:
tree_folder = tree_folder_raw
Expand Down
Loading
Loading