Spaces:
No application file
No application file
| #!/usr/bin/env python | |
| # coding=utf-8 | |
| # Copyright 2025 The HuggingFace Inc. team. All rights reserved. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import argparse | |
| import os | |
| from dotenv import load_dotenv | |
| from rich.console import Console | |
| from rich.panel import Panel | |
| from rich.prompt import Confirm, Prompt | |
| from rich.rule import Rule | |
| from rich.table import Table | |
| from smolagents import ( | |
| CodeAgent, | |
| InferenceClientModel, | |
| LiteLLMModel, | |
| Model, | |
| OpenAIModel, | |
| Tool, | |
| ToolCallingAgent, | |
| TransformersModel, | |
| ) | |
| from smolagents.default_tools import TOOL_MAPPING | |
| console = Console() | |
| leopard_prompt = "How many seconds would it take for a leopard at full speed to run through Pont des Arts?" | |
| def parse_arguments(): | |
| parser = argparse.ArgumentParser(description="Run a CodeAgent with all specified parameters") | |
| parser.add_argument( | |
| "prompt", | |
| type=str, | |
| nargs="?", | |
| default=None, | |
| help="The prompt to run with the agent. If no prompt is provided, interactive mode will be launched to guide user through agent setup", | |
| ) | |
| parser.add_argument( | |
| "--model-type", | |
| type=str, | |
| default="InferenceClientModel", | |
| help="The model type to use (e.g., InferenceClientModel, OpenAIModel, LiteLLMModel, TransformersModel)", | |
| ) | |
| parser.add_argument( | |
| "--action-type", | |
| type=str, | |
| default="code", | |
| help="The action type to use (e.g., code, tool_calling)", | |
| ) | |
| parser.add_argument( | |
| "--model-id", | |
| type=str, | |
| default="Qwen/Qwen3-Next-80B-A3B-Thinking", | |
| help="The model ID to use for the specified model type", | |
| ) | |
| parser.add_argument( | |
| "--imports", | |
| nargs="*", # accepts zero or more arguments | |
| default=[], | |
| help="Space-separated list of imports to authorize (e.g., 'numpy pandas')", | |
| ) | |
| parser.add_argument( | |
| "--tools", | |
| nargs="*", | |
| default=["web_search"], | |
| help="Space-separated list of tools that the agent can use (e.g., 'tool1 tool2 tool3')", | |
| ) | |
| parser.add_argument( | |
| "--verbosity-level", | |
| type=int, | |
| default=1, | |
| help="The verbosity level, as an int in [0, 1, 2].", | |
| ) | |
| group = parser.add_argument_group("api options", "Options for API-based model types") | |
| group.add_argument( | |
| "--provider", | |
| type=str, | |
| default=None, | |
| help="The inference provider to use for the model", | |
| ) | |
| group.add_argument( | |
| "--api-base", | |
| type=str, | |
| help="The base URL for the model", | |
| ) | |
| group.add_argument( | |
| "--api-key", | |
| type=str, | |
| help="The API key for the model", | |
| ) | |
| return parser.parse_args() | |
| def interactive_mode(): | |
| """Run the CLI in interactive mode""" | |
| console.print( | |
| Panel.fit( | |
| "[bold magenta]🤖 SmolaGents CLI[/]\n[dim]Intelligent agents at your service[/]", border_style="magenta" | |
| ) | |
| ) | |
| console.print("\n[bold yellow]Welcome to smolagents![/] Let's set up your agent step by step.\n") | |
| # Get user input step by step | |
| console.print(Rule("[bold yellow]⚙️ Configuration", style="bold yellow")) | |
| # Get agent action type | |
| action_type = Prompt.ask( | |
| "[bold white]What action type would you like to use? 'code' or 'tool_calling'?[/]", | |
| default="code", | |
| choices=["code", "tool_calling"], | |
| ) | |
| # Show available tools | |
| tools_table = Table(title="[bold yellow]🛠️ Available Tools", show_header=True, header_style="bold yellow") | |
| tools_table.add_column("Tool Name", style="bold yellow") | |
| tools_table.add_column("Description", style="white") | |
| for tool_name, tool_class in TOOL_MAPPING.items(): | |
| # Get description from the tool class if available | |
| try: | |
| tool_instance = tool_class() | |
| description = getattr(tool_instance, "description", "No description available") | |
| except Exception: | |
| description = "Built-in tool" | |
| tools_table.add_row(tool_name, description) | |
| console.print(tools_table) | |
| console.print( | |
| "\n[dim]You can also use HuggingFace Spaces by providing the full path (e.g., 'username/spacename')[/]" | |
| ) | |
| console.print("[dim]Enter tool names separated by spaces (e.g., 'web_search python_interpreter')[/]") | |
| tools_input = Prompt.ask("[bold white]Select tools for your agent[/]", default="web_search") | |
| tools = tools_input.split() | |
| # Get model configuration | |
| console.print("\n[bold yellow]Model Configuration:[/]") | |
| model_type = Prompt.ask( | |
| "[bold]Model type[/]", | |
| default="InferenceClientModel", | |
| choices=["InferenceClientModel", "OpenAIServerModel", "LiteLLMModel", "TransformersModel"], | |
| ) | |
| model_id = Prompt.ask("[bold white]Model ID[/]", default="Qwen/Qwen2.5-Coder-32B-Instruct") | |
| # Optional configurations | |
| provider = None | |
| api_base = None | |
| api_key = None | |
| imports = [] | |
| action_type = "code" | |
| if Confirm.ask("\n[bold white]Configure advanced options?[/]", default=False): | |
| if model_type in ["InferenceClientModel", "OpenAIServerModel", "LiteLLMModel"]: | |
| provider = Prompt.ask("[bold white]Provider[/]", default="") | |
| api_base = Prompt.ask("[bold white]API Base URL[/]", default="") | |
| api_key = Prompt.ask("[bold white]API Key[/]", default="", password=True) | |
| imports_input = Prompt.ask("[bold white]Additional imports (space-separated)[/]", default="") | |
| if imports_input: | |
| imports = imports_input.split() | |
| # Get prompt | |
| prompt = Prompt.ask( | |
| "[bold white]Now the final step; what task would you like the agent to perform?[/]", default=leopard_prompt | |
| ) | |
| return prompt, tools, model_type, model_id, provider, api_base, api_key, imports, action_type | |
| def load_model( | |
| model_type: str, | |
| model_id: str, | |
| api_base: str | None = None, | |
| api_key: str | None = None, | |
| provider: str | None = None, | |
| ) -> Model: | |
| if model_type == "OpenAIModel": | |
| return OpenAIModel( | |
| api_key=api_key or os.getenv("FIREWORKS_API_KEY"), | |
| api_base=api_base or "https://api.fireworks.ai/inference/v1", | |
| model_id=model_id, | |
| ) | |
| elif model_type == "LiteLLMModel": | |
| return LiteLLMModel( | |
| model_id=model_id, | |
| api_key=api_key, | |
| api_base=api_base, | |
| ) | |
| elif model_type == "TransformersModel": | |
| return TransformersModel(model_id=model_id, device_map="auto") | |
| elif model_type == "InferenceClientModel": | |
| return InferenceClientModel( | |
| model_id=model_id, | |
| token=api_key or os.getenv("HF_API_KEY"), | |
| provider=provider, | |
| ) | |
| else: | |
| raise ValueError(f"Unsupported model type: {model_type}") | |
| def run_smolagent( | |
| prompt: str, | |
| tools: list[str], | |
| model_type: str, | |
| model_id: str, | |
| api_base: str | None = None, | |
| api_key: str | None = None, | |
| imports: list[str] | None = None, | |
| provider: str | None = None, | |
| action_type: str = "code", | |
| ) -> None: | |
| load_dotenv() | |
| model = load_model(model_type, model_id, api_base=api_base, api_key=api_key, provider=provider) | |
| available_tools = [] | |
| for tool_name in tools: | |
| if "/" in tool_name: | |
| space_name = tool_name.split("/")[-1].lower().replace("-", "_").replace(".", "_") | |
| description = f"Tool loaded from Hugging Face Space: {tool_name}" | |
| available_tools.append(Tool.from_space(space_id=tool_name, name=space_name, description=description)) | |
| else: | |
| if tool_name in TOOL_MAPPING: | |
| available_tools.append(TOOL_MAPPING[tool_name]()) | |
| else: | |
| raise ValueError(f"Tool {tool_name} is not recognized either as a default tool or a Space.") | |
| if action_type == "code": | |
| agent = CodeAgent( | |
| tools=available_tools, | |
| model=model, | |
| additional_authorized_imports=imports, | |
| stream_outputs=True, | |
| ) | |
| elif action_type == "tool_calling": | |
| agent = ToolCallingAgent(tools=available_tools, model=model, stream_outputs=True) | |
| else: | |
| raise ValueError(f"Unsupported action type: {action_type}") | |
| agent.run(prompt) | |
| def main() -> None: | |
| args = parse_arguments() | |
| # Check if we should run in interactive mode | |
| # Interactive mode is triggered when no prompt is provided | |
| if args.prompt is None: | |
| prompt, tools, model_type, model_id, provider, api_base, api_key, imports, action_type = interactive_mode() | |
| else: | |
| prompt = args.prompt | |
| tools = args.tools | |
| model_type = args.model_type | |
| model_id = args.model_id | |
| provider = args.provider | |
| api_base = args.api_base | |
| api_key = args.api_key | |
| imports = args.imports | |
| action_type = args.action_type | |
| run_smolagent( | |
| prompt, | |
| tools, | |
| model_type, | |
| model_id, | |
| provider=provider, | |
| api_base=api_base, | |
| api_key=api_key, | |
| imports=imports, | |
| action_type=action_type, | |
| ) | |
| if __name__ == "__main__": | |
| main() | |