Skip to content

Commit 4dbab6d

Browse files
Dialog mode cli (#1540)
1 parent 9c29c0e commit 4dbab6d

File tree

6 files changed

+172
-21
lines changed

6 files changed

+172
-21
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,20 @@ You can run agents from CLI using two commands: `smolagent` and `webagent`.
178178
`smolagent` is a generalist command to run a multi-step `CodeAgent` that can be equipped with various tools.
179179

180180
```bash
181+
# Run with direct prompt and options
181182
smolagent "Plan a trip to Tokyo, Kyoto and Osaka between Mar 28 and Apr 7." --model-type "InferenceClientModel" --model-id "Qwen/Qwen3-Next-80B-A3B-Thinking" --imports pandas numpy --tools web_search
183+
184+
# Run in interactive mode (launches setup wizard when no prompt provided)
185+
smolagent
182186
```
183187

188+
Interactive mode guides you through:
189+
- Agent type selection (CodeAgent vs ToolCallingAgent)
190+
- Tool selection from available toolbox
191+
- Model configuration (type, ID, API settings)
192+
- Advanced options like additional imports
193+
- Task prompt input
194+
184195
Meanwhile `webagent` is a specific web-browsing agent using [helium](https://github.com/mherrmann/helium) (read more [here](https://github.com/huggingface/smolagents/blob/main/src/smolagents/vision_web_browser.py)).
185196

186197
For instance:

docs/source/en/guided_tour.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,18 @@ agent = ToolCallingAgent(tools=[WebSearchTool()], model=model)
104104
agent.run("Could you get me the title of the page at url 'https://huggingface.co/blog'?")
105105
```
106106

107+
## Using the CLI
108+
109+
You can quickly get started with smolagents using the command line interface:
110+
111+
```bash
112+
# Run with direct prompt and options
113+
smolagent "Plan a trip to Tokyo, Kyoto and Osaka between Mar 28 and Apr 7." --model-type "InferenceClientModel" --model-id "Qwen/Qwen2.5-Coder-32B-Instruct" --imports "pandas numpy" --tools "web_search"
114+
115+
# Run in interactive mode: launches when no prompt is provided, will guide you through argument selection
116+
smolagent
117+
```
118+
107119
## Building your agent
108120

109121
To initialize a minimal agent, you need at least these two arguments:

src/smolagents/cli.py

Lines changed: 145 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,27 @@
1818
import os
1919

2020
from dotenv import load_dotenv
21+
from rich.console import Console
22+
from rich.panel import Panel
23+
from rich.prompt import Confirm, Prompt
24+
from rich.rule import Rule
25+
from rich.table import Table
2126

22-
from smolagents import CodeAgent, InferenceClientModel, LiteLLMModel, Model, OpenAIModel, Tool, TransformersModel
27+
from smolagents import (
28+
CodeAgent,
29+
InferenceClientModel,
30+
LiteLLMModel,
31+
Model,
32+
OpenAIModel,
33+
Tool,
34+
ToolCallingAgent,
35+
TransformersModel,
36+
)
2337
from smolagents.default_tools import TOOL_MAPPING
2438

2539

40+
console = Console()
41+
2642
leopard_prompt = "How many seconds would it take for a leopard at full speed to run through Pont des Arts?"
2743

2844

@@ -31,16 +47,22 @@ def parse_arguments():
3147
parser.add_argument(
3248
"prompt",
3349
type=str,
34-
nargs="?", # Makes it optional
35-
default=leopard_prompt,
36-
help="The prompt to run with the agent",
50+
nargs="?",
51+
default=None,
52+
help="The prompt to run with the agent. If no prompt is provided, interactive mode will be launched to guide user through agent setup",
3753
)
3854
parser.add_argument(
3955
"--model-type",
4056
type=str,
4157
default="InferenceClientModel",
4258
help="The model type to use (e.g., InferenceClientModel, OpenAIModel, LiteLLMModel, TransformersModel)",
4359
)
60+
parser.add_argument(
61+
"--action-type",
62+
type=str,
63+
default="code",
64+
help="The action type to use (e.g., code, tool_calling)",
65+
)
4466
parser.add_argument(
4567
"--model-id",
4668
type=str,
@@ -85,6 +107,84 @@ def parse_arguments():
85107
return parser.parse_args()
86108

87109

110+
def interactive_mode():
111+
"""Run the CLI in interactive mode"""
112+
console.print(
113+
Panel.fit(
114+
"[bold magenta]🤖 SmolaGents CLI[/]\n[dim]Intelligent agents at your service[/]", border_style="magenta"
115+
)
116+
)
117+
118+
console.print("\n[bold yellow]Welcome to smolagents![/] Let's set up your agent step by step.\n")
119+
120+
# Get user input step by step
121+
console.print(Rule("[bold yellow]⚙️ Configuration", style="bold yellow"))
122+
123+
# Get agent action type
124+
action_type = Prompt.ask(
125+
"[bold white]What action type would you like to use? 'code' or 'tool_calling'?[/]",
126+
default="code",
127+
choices=["code", "tool_calling"],
128+
)
129+
130+
# Show available tools
131+
tools_table = Table(title="[bold yellow]🛠️ Available Tools", show_header=True, header_style="bold yellow")
132+
tools_table.add_column("Tool Name", style="bold yellow")
133+
tools_table.add_column("Description", style="white")
134+
135+
for tool_name, tool_class in TOOL_MAPPING.items():
136+
# Get description from the tool class if available
137+
try:
138+
tool_instance = tool_class()
139+
description = getattr(tool_instance, "description", "No description available")
140+
except Exception:
141+
description = "Built-in tool"
142+
tools_table.add_row(tool_name, description)
143+
144+
console.print(tools_table)
145+
console.print(
146+
"\n[dim]You can also use HuggingFace Spaces by providing the full path (e.g., 'username/spacename')[/]"
147+
)
148+
149+
console.print("[dim]Enter tool names separated by spaces (e.g., 'web_search python_interpreter')[/]")
150+
tools_input = Prompt.ask("[bold white]Select tools for your agent[/]", default="web_search")
151+
tools = tools_input.split()
152+
153+
# Get model configuration
154+
console.print("\n[bold yellow]Model Configuration:[/]")
155+
model_type = Prompt.ask(
156+
"[bold]Model type[/]",
157+
default="InferenceClientModel",
158+
choices=["InferenceClientModel", "OpenAIServerModel", "LiteLLMModel", "TransformersModel"],
159+
)
160+
161+
model_id = Prompt.ask("[bold white]Model ID[/]", default="Qwen/Qwen2.5-Coder-32B-Instruct")
162+
163+
# Optional configurations
164+
provider = None
165+
api_base = None
166+
api_key = None
167+
imports = []
168+
action_type = "code"
169+
170+
if Confirm.ask("\n[bold white]Configure advanced options?[/]", default=False):
171+
if model_type in ["InferenceClientModel", "OpenAIServerModel", "LiteLLMModel"]:
172+
provider = Prompt.ask("[bold white]Provider[/]", default="")
173+
api_base = Prompt.ask("[bold white]API Base URL[/]", default="")
174+
api_key = Prompt.ask("[bold white]API Key[/]", default="", password=True)
175+
176+
imports_input = Prompt.ask("[bold white]Additional imports (space-separated)[/]", default="")
177+
if imports_input:
178+
imports = imports_input.split()
179+
180+
# Get prompt
181+
prompt = Prompt.ask(
182+
"[bold white]Now the final step; what task would you like the agent to perform?[/]", default=leopard_prompt
183+
)
184+
185+
return prompt, tools, model_type, model_id, provider, api_base, api_key, imports, action_type
186+
187+
88188
def load_model(
89189
model_type: str,
90190
model_id: str,
@@ -125,38 +225,68 @@ def run_smolagent(
125225
api_key: str | None = None,
126226
imports: list[str] | None = None,
127227
provider: str | None = None,
228+
action_type: str = "code",
128229
) -> None:
129230
load_dotenv()
130231

131232
model = load_model(model_type, model_id, api_base=api_base, api_key=api_key, provider=provider)
132233

133234
available_tools = []
235+
134236
for tool_name in tools:
135237
if "/" in tool_name:
136-
available_tools.append(Tool.from_space(tool_name))
238+
available_tools.append(
239+
Tool.from_space(tool_name, name=tool_name.split("/")[-1], description=f"Tool from space: {tool_name}")
240+
)
137241
else:
138242
if tool_name in TOOL_MAPPING:
139243
available_tools.append(TOOL_MAPPING[tool_name]())
140244
else:
141245
raise ValueError(f"Tool {tool_name} is not recognized either as a default tool or a Space.")
142246

143-
print(f"Running agent with these tools: {tools}")
144-
agent = CodeAgent(tools=available_tools, model=model, additional_authorized_imports=imports)
247+
if action_type == "code":
248+
agent = CodeAgent(
249+
tools=available_tools,
250+
model=model,
251+
additional_authorized_imports=imports,
252+
stream_outputs=True,
253+
)
254+
elif action_type == "tool_calling":
255+
agent = ToolCallingAgent(tools=available_tools, model=model, stream_outputs=True)
256+
else:
257+
raise ValueError(f"Unsupported action type: {action_type}")
145258

146259
agent.run(prompt)
147260

148261

149262
def main() -> None:
150263
args = parse_arguments()
264+
265+
# Check if we should run in interactive mode
266+
# Interactive mode is triggered when no prompt is provided
267+
if args.prompt is None:
268+
prompt, tools, model_type, model_id, provider, api_base, api_key, imports, action_type = interactive_mode()
269+
else:
270+
prompt = args.prompt
271+
tools = args.tools
272+
model_type = args.model_type
273+
model_id = args.model_id
274+
provider = args.provider
275+
api_base = args.api_base
276+
api_key = args.api_key
277+
imports = args.imports
278+
action_type = args.action_type
279+
151280
run_smolagent(
152-
args.prompt,
153-
args.tools,
154-
args.model_type,
155-
args.model_id,
156-
provider=args.provider,
157-
api_base=args.api_base,
158-
api_key=args.api_key,
159-
imports=args.imports,
281+
prompt,
282+
tools,
283+
model_type,
284+
model_id,
285+
provider=provider,
286+
api_base=api_base,
287+
api_key=api_key,
288+
imports=imports,
289+
action_type=action_type,
160290
)
161291

162292

src/smolagents/monitoring.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ def log_code(self, title: str, content: str, level: int = LogLevel.INFO) -> None
190190
def log_rule(self, title: str, level: int = LogLevel.INFO) -> None:
191191
self.log(
192192
Rule(
193-
"[bold]" + title,
193+
"[bold white]" + title,
194194
characters="━",
195195
style=YELLOW_HEX,
196196
),

src/smolagents/tools.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ def from_code(cls, tool_code: str, **kwargs):
600600
def from_space(
601601
space_id: str,
602602
name: str,
603-
description: str,
603+
description: str = "",
604604
api_name: str | None = None,
605605
token: str | None = None,
606606
):
@@ -649,7 +649,7 @@ def __init__(
649649
self,
650650
space_id: str,
651651
name: str,
652-
description: str,
652+
description: str = "",
653653
api_name: str | None = None,
654654
token: str | None = None,
655655
):

tests/test_cli.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,11 @@ def test_cli_main(capsys):
7777
"tools": [],
7878
"model": "mock_model",
7979
"additional_authorized_imports": None,
80+
"stream_outputs": True,
8081
}
8182
# agent.run
8283
assert len(mock_code_agent.return_value.run.call_args_list) == 1
8384
assert mock_code_agent.return_value.run.call_args.args == ("test_prompt",)
84-
# print
85-
captured = capsys.readouterr()
86-
assert "Running agent with these tools: []" in captured.out
8785

8886

8987
def test_vision_web_browser_main():

0 commit comments

Comments
 (0)