Repository: huggingface/smolagents

Issue:
[Bug] CodeAgent's system prompt does not contain authorized_imports
I think there is a small bug with the CodeAgent's system prompt. 

In `agent.run` [initialize_system_prompt()](https://github.com/huggingface/smolagents/blob/2a69f1574e3c1ddee11d8344b1cd1272608fe5f7/src/smolagents/agents.py#L493)  is called again, which means that the system prompt does not contain correct the following list of `modules: {{authorized_imports}}` that python can import. As that is replaced in the child classes init method.

Possible solutions:

-  `self.initialize_system_prompt()` can be removed from the step method, as it is already called in `__init__`
- `self.initialize_system_prompt()` can be overridden by `CodeAgent` to include the following:
```
        self.system_prompt = self.system_prompt.replace(
            "{{authorized_imports}}",
            "You can import from any package you want."
            if "*" in self.authorized_imports
            else str(self.authorized_imports),
        )
```

Discussion:
aymeric-roucher: Hello Ed! See my comment here: https://github.com/huggingface/smolagents/issues/223#issuecomment-2596143925

> Regarding which solution's the best: if people change tools, the system prompt should be updated to adapt to the new tools before running, so I think it's better to keep the `self.initialize_system_prompt()` at the start of `run()`
> So I think the best solution the one where you add custom logic in the CodeAgent's overridden `initialize_system_prompt`.

Patch:
diff --git a/docs/source/en/examples/multiagents.md b/docs/source/en/examples/multiagents.md
index 7901de2..c4bb514 100644
--- a/docs/source/en/examples/multiagents.md
+++ b/docs/source/en/examples/multiagents.md
@@ -64,7 +64,7 @@ model_id = "Qwen/Qwen2.5-Coder-32B-Instruct"
 
 ## 🔍 Create a web search tool
 
-For web browsing, we can already use our pre-existing [`DuckDuckGoSearchTool`](https://github.com/huggingface/smolagents/blob/main/src/smolagents/default_tools/search.py) tool to provide a Google search equivalent.
+For web browsing, we can already use our pre-existing [`DuckDuckGoSearchTool`](https://github.com/huggingface/smolagents/blob/main/src/smolagents/default_tools.py#L151-L176) tool to provide a Google search equivalent.
 
 But then we will also need to be able to peak into the page found by the `DuckDuckGoSearchTool`.
 To do so, we could import the library's built-in `VisitWebpageTool`, but we will build it again to see how it's done.
@@ -196,4 +196,4 @@ Seems like we'll need some sizeable powerplants if the [scaling hypothesis](http
 
 Our agents managed to efficiently collaborate towards solving the task! ✅
 
-💡 You can easily extend this orchestration to more agents: one does the code execution, one the web search, one handles file loadings...
\ No newline at end of file
+💡 You can easily extend this orchestration to more agents: one does the code execution, one the web search, one handles file loadings...
diff --git a/docs/source/en/reference/tools.md b/docs/source/en/reference/tools.md
index 022ad35..9d78774 100644
--- a/docs/source/en/reference/tools.md
+++ b/docs/source/en/reference/tools.md
@@ -57,6 +57,10 @@ contains the API docs for the underlying classes.
 
 [[autodoc]] VisitWebpageTool
 
+### UserInputTool
+
+[[autodoc]] UserInputTool
+
 ## ToolCollection
 
 [[autodoc]] ToolCollection
diff --git a/docs/source/en/tutorials/tools.md b/docs/source/en/tutorials/tools.md
index 41556fa..5e6ced3 100644
--- a/docs/source/en/tutorials/tools.md
+++ b/docs/source/en/tutorials/tools.md
@@ -131,7 +131,7 @@ And voilà, here's your image! 🏖️
 
 <img src="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/sunny_beach.webp">
 
-Then you can use this tool just like any other tool.  For example, let's improve the prompt  `a rabbit wearing a space suit` and generate an image of it.
+Then you can use this tool just like any other tool.  For example, let's improve the prompt  `a rabbit wearing a space suit` and generate an image of it. This example also shows how you can pass additional arguments to the agent.
 
 ```python
 from smolagents import CodeAgent, HfApiModel
@@ -140,7 +140,7 @@ model = HfApiModel("Qwen/Qwen2.5-Coder-32B-Instruct")
 agent = CodeAgent(tools=[image_generation_tool], model=model)
 
 agent.run(
-    "Improve this prompt, then generate an image of it.", prompt='A rabbit wearing a space suit'
+    "Improve this prompt, then generate an image of it.", additional_args={'user_prompt': 'A rabbit wearing a space suit'}
 )
 ```
 
diff --git a/docs/source/zh/examples/multiagents.md b/docs/source/zh/examples/multiagents.md
index 4ea4e51..9c3af8e 100644
--- a/docs/source/zh/examples/multiagents.md
+++ b/docs/source/zh/examples/multiagents.md
@@ -64,7 +64,7 @@ model_id = "Qwen/Qwen2.5-Coder-32B-Instruct"
 
 ## 🔍 Create a web search tool
 
-For web browsing, we can already use our pre-existing [`DuckDuckGoSearchTool`](https://github.com/huggingface/smolagents/blob/main/src/smolagents/default_tools/search.py) tool to provide a Google search equivalent.
+For web browsing, we can already use our pre-existing [`DuckDuckGoSearchTool`](https://github.com/huggingface/smolagents/blob/main/src/smolagents/default_tools.py#L151-L176) tool to provide a Google search equivalent.
 
 But then we will also need to be able to peak into the page found by the `DuckDuckGoSearchTool`.
 To do so, we could import the library's built-in `VisitWebpageTool`, but we will build it again to see how it's done.
@@ -196,4 +196,4 @@ Seems like we'll need some sizeable powerplants if the [scaling hypothesis](http
 
 Our agents managed to efficiently collaborate towards solving the task! ✅
 
-💡 You can easily extend this orchestration to more agents: one does the code execution, one the web search, one handles file loadings...
\ No newline at end of file
+💡 You can easily extend this orchestration to more agents: one does the code execution, one the web search, one handles file loadings...
diff --git a/src/smolagents/agents.py b/src/smolagents/agents.py
index f17daa9..20c67b4 100644
--- a/src/smolagents/agents.py
+++ b/src/smolagents/agents.py
@@ -895,30 +895,17 @@ class CodeAgent(MultiStepAgent):
     ):
         if system_prompt is None:
             system_prompt = CODE_SYSTEM_PROMPT
-        super().__init__(
-            tools=tools,
-            model=model,
-            system_prompt=system_prompt,
-            grammar=grammar,
-            planning_interval=planning_interval,
-            **kwargs,
-        )
+
         self.additional_authorized_imports = (
             additional_authorized_imports if additional_authorized_imports else []
         )
         self.authorized_imports = list(
             set(BASE_BUILTIN_MODULES) | set(self.additional_authorized_imports)
         )
-        if "{{authorized_imports}}" not in self.system_prompt:
+        if "{{authorized_imports}}" not in system_prompt:
             raise AgentError(
                 "Tag '{{authorized_imports}}' should be provided in the prompt."
             )
-        self.system_prompt = self.system_prompt.replace(
-            "{{authorized_imports}}",
-            "You can import from any package you want."
-            if "*" in self.authorized_imports
-            else str(self.authorized_imports),
-        )
 
         if "*" in self.additional_authorized_imports:
             self.logger.log(
@@ -926,6 +913,14 @@ class CodeAgent(MultiStepAgent):
                 0,
             )
 
+        super().__init__(
+            tools=tools,
+            model=model,
+            system_prompt=system_prompt,
+            grammar=grammar,
+            planning_interval=planning_interval,
+            **kwargs,
+        )
         if use_e2b_executor and len(self.managed_agents) > 0:
             raise Exception(
                 f"You passed both {use_e2b_executor=} and some managed agents. Managed agents is not yet supported with remote code execution."
@@ -944,6 +939,16 @@ class CodeAgent(MultiStepAgent):
                 all_tools,
             )
 
+    def initialize_system_prompt(self):
+        super().initialize_system_prompt()
+        self.system_prompt = self.system_prompt.replace(
+            "{{authorized_imports}}",
+            "You can import from any package you want."
+            if "*" in self.authorized_imports
+            else str(self.authorized_imports),
+        )
+        return self.system_prompt
+
     def step(self, log_entry: ActionStep) -> Union[None, Any]:
         """
         Perform one step in the ReAct framework: the agent thinks, acts, and observes the result.
diff --git a/src/smolagents/default_tools.py b/src/smolagents/default_tools.py
index 3a6be33..14a46ae 100644
--- a/src/smolagents/default_tools.py
+++ b/src/smolagents/default_tools.py
@@ -144,7 +144,7 @@ class UserInputTool(Tool):
     output_type = "string"
 
     def forward(self, question):
-        user_input = input(f"{question} => ")
+        user_input = input(f"{question} => Type your answer here:")
         return user_input
 
 
diff --git a/src/smolagents/gradio_ui.py b/src/smolagents/gradio_ui.py
index e6d52e5..e04056d 100644
--- a/src/smolagents/gradio_ui.py
+++ b/src/smolagents/gradio_ui.py
@@ -120,21 +120,15 @@ class GradioUI:
         """
 
         if file is None:
-            return gr.Textbox(
-                "No file uploaded", visible=True
-            ), file_uploads_log
+            return gr.Textbox("No file uploaded", visible=True), file_uploads_log
 
         try:
             mime_type, _ = mimetypes.guess_type(file.name)
         except Exception as e:
-            return gr.Textbox(
-                f"Error: {e}", visible=True
-            ), file_uploads_log
+            return gr.Textbox(f"Error: {e}", visible=True), file_uploads_log
 
         if mime_type not in allowed_file_types:
-            return gr.Textbox(
-                "File type disallowed", visible=True
-            ), file_uploads_log
+            return gr.Textbox("File type disallowed", visible=True), file_uploads_log
 
         # Sanitize file name
         original_name = os.path.basename(file.name)
@@ -184,6 +178,7 @@ class GradioUI:
                     None,
                     "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/smolagents/mascot_smol.png",
                 ),
+                resizeable=True,
             )
             # If an upload folder is provided, enable the upload feature
             if self.file_upload_folder is not None:
diff --git a/src/smolagents/local_python_executor.py b/src/smolagents/local_python_executor.py
index 1b9b35d..069d526 100644
--- a/src/smolagents/local_python_executor.py
+++ b/src/smolagents/local_python_executor.py
@@ -21,6 +21,7 @@ import math
 import re
 from collections.abc import Mapping
 from importlib import import_module
+from types import ModuleType
 from typing import Any, Callable, Dict, List, Optional, Tuple
 
 import numpy as np
@@ -1046,12 +1047,75 @@ def evaluate_with(
             context.__exit__(None, None, None)
 
 
+def get_safe_module(unsafe_module, dangerous_patterns, visited=None):
+    """Creates a safe copy of a module or returns the original if it's a function"""
+    # If it's a function or non-module object, return it directly
+    if not isinstance(unsafe_module, ModuleType):
+        return unsafe_module
+
+    # Handle circular references: Initialize visited set for the first call
+    if visited is None:
+        visited = set()
+
+    module_id = id(unsafe_module)
+    if module_id in visited:
+        return unsafe_module  # Return original for circular refs
+
+    visited.add(module_id)
+
+    # Create new module for actual modules
+    safe_module = ModuleType(unsafe_module.__name__)
+
+    # Copy all attributes by reference, recursively checking modules
+    for attr_name in dir(unsafe_module):
+        # Skip dangerous patterns at any level
+        if any(
+            pattern in f"{unsafe_module.__name__}.{attr_name}"
+            for pattern in dangerous_patterns
+        ):
+            continue
+
+        attr_value = getattr(unsafe_module, attr_name)
+
+        # Recursively process nested modules, passing visited set
+        if isinstance(attr_value, ModuleType):
+            attr_value = get_safe_module(
+                attr_value, dangerous_patterns, visited=visited
+            )
+
+        setattr(safe_module, attr_name, attr_value)
+
+    return safe_module
+
+
 def import_modules(expression, state, authorized_imports):
+    dangerous_patterns = (
+        "_os",
+        "os",
+        "subprocess",
+        "_subprocess",
+        "pty",
+        "system",
+        "popen",
+        "spawn",
+        "shutil",
+        "glob",
+        "pathlib",
+        "io",
+        "socket",
+        "compile",
+        "eval",
+        "exec",
+        "multiprocessing",
+    )
+
     def check_module_authorized(module_name):
         if "*" in authorized_imports:
             return True
         else:
             module_path = module_name.split(".")
+            if any([module in dangerous_patterns for module in module_path]):
+                return False
             module_subpaths = [
                 ".".join(module_path[:i]) for i in range(1, len(module_path) + 1)
             ]
@@ -1060,8 +1124,10 @@ def import_modules(expression, state, authorized_imports):
     if isinstance(expression, ast.Import):
         for alias in expression.names:
             if check_module_authorized(alias.name):
-                module = import_module(alias.name)
-                state[alias.asname or alias.name] = module
+                raw_module = import_module(alias.name)
+                state[alias.asname or alias.name] = get_safe_module(
+                    raw_module, dangerous_patterns
+                )
             else:
                 raise InterpreterError(
                     f"Import of {alias.name} is not allowed. Authorized imports are: {str(authorized_imports)}"
@@ -1069,11 +1135,13 @@ def import_modules(expression, state, authorized_imports):
         return None
     elif isinstance(expression, ast.ImportFrom):
         if check_module_authorized(expression.module):
-            module = __import__(
+            raw_module = __import__(
                 expression.module, fromlist=[alias.name for alias in expression.names]
             )
             for alias in expression.names:
-                state[alias.asname or alias.name] = getattr(module, alias.name)
+                state[alias.asname or alias.name] = get_safe_module(
+                    getattr(raw_module, alias.name), dangerous_patterns
+                )
         else:
             raise InterpreterError(f"Import from {expression.module} is not allowed.")
         return None
diff --git a/src/smolagents/models.py b/src/smolagents/models.py
index 6a1ae77..f07d799 100644
--- a/src/smolagents/models.py
+++ b/src/smolagents/models.py
@@ -222,7 +222,6 @@ class Model:
         messages: List[Dict[str, str]],
         stop_sequences: Optional[List[str]] = None,
         grammar: Optional[str] = None,
-        max_tokens: int = 1500,
     ) -> ChatMessage:
         """Process the input messages and return the model's response.
 
@@ -233,8 +232,6 @@ class Model:
                 A list of strings that will stop the generation if encountered in the model's output.
             grammar (`str`, *optional*):
                 The grammar or formatting structure to use in the model's response.
-            max_tokens (`int`, *optional*):
-                The maximum count of tokens to generate.
         Returns:
             `str`: The text content of the model's response.
         """
@@ -244,7 +241,7 @@ class Model:
 class HfApiModel(Model):
     """A class to interact with Hugging Face's Inference API for language model interaction.
 
-    This engine allows you to communicate with Hugging Face's models using the Inference API. It can be used in both serverless mode or with a dedicated endpoint, supporting features like stop sequences and grammar customization.
+    This model allows you to communicate with Hugging Face's models using the Inference API. It can be used in both serverless mode or with a dedicated endpoint, supporting features like stop sequences and grammar customization.
 
     Parameters:
         model_id (`str`, *optional*, defaults to `"Qwen/Qwen2.5-Coder-32B-Instruct"`):
@@ -265,9 +262,10 @@ class HfApiModel(Model):
     >>> engine = HfApiModel(
     ...     model_id="Qwen/Qwen2.5-Coder-32B-Instruct",
     ...     token="your_hf_token_here",
+    ...     max_tokens=5000,
     ... )
     >>> messages = [{"role": "user", "content": "Explain quantum mechanics in simple terms."}]
-    >>> response = engine(messages, stop_sequences=["END"], max_tokens=1500)
+    >>> response = engine(messages, stop_sequences=["END"])
     >>> print(response)
     "Quantum mechanics is the branch of physics that studies..."
     ```
@@ -279,6 +277,7 @@ class HfApiModel(Model):
         token: Optional[str] = None,
         timeout: Optional[int] = 120,
         temperature: float = 0.5,
+        **kwargs,
     ):
         super().__init__()
         self.model_id = model_id
@@ -286,13 +285,13 @@ class HfApiModel(Model):
             token = os.getenv("HF_TOKEN")
         self.client = InferenceClient(self.model_id, token=token, timeout=timeout)
         self.temperature = temperature
+        self.kwargs = kwargs
 
     def __call__(
         self,
         messages: List[Dict[str, str]],
         stop_sequences: Optional[List[str]] = None,
         grammar: Optional[str] = None,
-        max_tokens: int = 1500,
         tools_to_call_from: Optional[List[Tool]] = None,
     ) -> ChatMessage:
         """
@@ -308,16 +307,16 @@ class HfApiModel(Model):
                 tools=[get_json_schema(tool) for tool in tools_to_call_from],
                 tool_choice="auto",
                 stop=stop_sequences,
-                max_tokens=max_tokens,
                 temperature=self.temperature,
+                **self.kwargs,
             )
         else:
             response = self.client.chat.completions.create(
                 model=self.model_id,
                 messages=messages,
                 stop=stop_sequences,
-                max_tokens=max_tokens,
                 temperature=self.temperature,
+                **self.kwargs,
             )
         self.last_input_token_count = response.usage.prompt_tokens
         self.last_output_token_count = response.usage.completion_tokens
@@ -325,16 +324,47 @@ class HfApiModel(Model):
 
 
 class TransformersModel(Model):
-    """This engine initializes a model and tokenizer from the given `model_id`.
+    """A class to interact with Hugging Face's Inference API for language model interaction.
+
+    This model allows you to communicate with Hugging Face's models using the Inference API. It can be used in both serverless mode or with a dedicated endpoint, supporting features like stop sequences and grammar customization.
 
     Parameters:
-        model_id (`str`, *optional*, defaults to `"HuggingFaceTB/SmolLM2-1.7B-Instruct"`):
+        model_id (`str`, *optional*, defaults to `"Qwen/Qwen2.5-Coder-32B-Instruct"`):
             The Hugging Face model ID to be used for inference. This can be a path or model identifier from the Hugging Face model hub.
-        device (`str`, optional, defaults to `"cuda"` if available, else `"cpu"`.):
-            The device to load the model on (`"cpu"` or `"cuda"`).
+        device_map (`str`, *optional*):
+            The device_map to initialize your model with.
+        torch_dtype (`str`, *optional*):
+            The torch_dtype to initialize your model with.
+        trust_remote_code (bool):
+            Some models on the Hub require running remote code: for this model, you would have to set this flag to True.
+        kwargs (dict, *optional*):
+            Any additional keyword arguments that you want to use in model.generate(), for instance `max_new_tokens` or `device`.
+    Raises:
+        ValueError:
+            If the model name is not provided.
+
+    Example:
+    ```python
+    >>> engine = TransformersModel(
+    ...     model_id="Qwen/Qwen2.5-Coder-32B-Instruct",
+    ...     device="cuda",
+    ...     max_new_tokens=5000,
+    ... )
+    >>> messages = [{"role": "user", "content": "Explain quantum mechanics in simple terms."}]
+    >>> response = engine(messages, stop_sequences=["END"])
+    >>> print(response)
+    "Quantum mechanics is the branch of physics that studies..."
+    ```
     """
 
-    def __init__(self, model_id: Optional[str] = None, device: Optional[str] = None):
+    def __init__(
+        self,
+        model_id: Optional[str] = None,
+        device_map: Optional[str] = None,
+        torch_dtype: Optional[str] = None,
+        trust_remote_code: bool = False,
+        **kwargs,
+    ):
         super().__init__()
         if not is_torch_available():
             raise ImportError("Please install torch in order to use TransformersModel.")
@@ -347,14 +377,17 @@ class TransformersModel(Model):
                 f"`model_id`not provided, using this default tokenizer for token counts: '{model_id}'"
             )
         self.model_id = model_id
-        if device is None:
-            device = "cuda" if torch.cuda.is_available() else "cpu"
-        self.device = device
-        logger.info(f"Using device: {self.device}")
+        self.kwargs = kwargs
+        if device_map is None:
+            device_map = "cuda" if torch.cuda.is_available() else "cpu"
+        logger.info(f"Using device: {device_map}")
         try:
             self.tokenizer = AutoTokenizer.from_pretrained(model_id)
             self.model = AutoModelForCausalLM.from_pretrained(
-                model_id, device_map=self.device
+                model_id,
+                device_map=device_map,
+                torch_dtype=torch_dtype,
+                trust_remote_code=trust_remote_code,
             )
         except Exception as e:
             logger.warning(
@@ -363,7 +396,7 @@ class TransformersModel(Model):
             self.model_id = default_model_id
             self.tokenizer = AutoTokenizer.from_pretrained(default_model_id)
             self.model = AutoModelForCausalLM.from_pretrained(
-                model_id, device_map=self.device
+                model_id, device_map=device_map, torch_dtype=torch_dtype
             )
 
     def make_stopping_criteria(self, stop_sequences: List[str]) -> StoppingCriteriaList:
@@ -397,7 +430,6 @@ class TransformersModel(Model):
         messages: List[Dict[str, str]],
         stop_sequences: Optional[List[str]] = None,
         grammar: Optional[str] = None,
-        max_tokens: int = 1500,
         tools_to_call_from: Optional[List[Tool]] = None,
     ) -> ChatMessage:
         messages = get_clean_message_list(
@@ -422,10 +454,10 @@ class TransformersModel(Model):
 
         out = self.model.generate(
             **prompt_tensor,
-            max_new_tokens=max_tokens,
             stopping_criteria=(
                 self.make_stopping_criteria(stop_sequences) if stop_sequences else None
             ),
+            **self.kwargs,
         )
         generated_tokens = out[0, count_prompt_tokens:]
         output = self.tokenizer.decode(generated_tokens, skip_special_tokens=True)
@@ -458,6 +490,19 @@ class TransformersModel(Model):
 
 
 class LiteLLMModel(Model):
+    """This model connects to [LiteLLM](https://www.litellm.ai/) as a gateway to hundreds of LLMs.
+
+    Parameters:
+        model_id (`str`):
+            The model identifier to use on the server (e.g. "gpt-3.5-turbo").
+        api_base (`str`):
+            The base URL of the OpenAI-compatible API server.
+        api_key (`str`):
+            The API key to use for authentication.
+        **kwargs:
+            Additional keyword arguments to pass to the OpenAI API.
+    """
+
     def __init__(
         self,
         model_id="anthropic/claude-3-5-sonnet-20240620",
@@ -482,7 +527,6 @@ class LiteLLMModel(Model):
         messages: List[Dict[str, str]],
         stop_sequences: Optional[List[str]] = None,
         grammar: Optional[str] = None,
-        max_tokens: int = 1500,
         tools_to_call_from: Optional[List[Tool]] = None,
     ) -> ChatMessage:
         messages = get_clean_message_list(
@@ -495,7 +539,6 @@ class LiteLLMModel(Model):
                 tools=[get_json_schema(tool) for tool in tools_to_call_from],
                 tool_choice="required",
                 stop=stop_sequences,
-                max_tokens=max_tokens,
                 api_base=self.api_base,
                 api_key=self.api_key,
                 **self.kwargs,
@@ -505,7 +548,6 @@ class LiteLLMModel(Model):
                 model=self.model_id,
                 messages=messages,
                 stop=stop_sequences,
-                max_tokens=max_tokens,
                 api_base=self.api_base,
                 api_key=self.api_key,
                 **self.kwargs,
@@ -516,7 +558,7 @@ class LiteLLMModel(Model):
 
 
 class OpenAIServerModel(Model):
-    """This engine connects to an OpenAI-compatible API server.
+    """This model connects to an OpenAI-compatible API server.
 
     Parameters:
         model_id (`str`):
@@ -525,8 +567,6 @@ class OpenAIServerModel(Model):
             The base URL of the OpenAI-compatible API server.
         api_key (`str`):
             The API key to use for authentication.
-        temperature (`float`, *optional*, defaults to 0.7):
-            Controls randomness in the model's responses. Values between 0 and 2.
         **kwargs:
             Additional keyword arguments to pass to the OpenAI API.
     """
@@ -536,7 +576,6 @@ class OpenAIServerModel(Model):
         model_id: str,
         api_base: str,
         api_key: str,
-        temperature: float = 0.7,
         **kwargs,
     ):
         super().__init__()
@@ -545,7 +584,6 @@ class OpenAIServerModel(Model):
             base_url=api_base,
             api_key=api_key,
         )
-        self.temperature = temperature
         self.kwargs = kwargs
 
     def __call__(
@@ -553,7 +591,6 @@ class OpenAIServerModel(Model):
         messages: List[Dict[str, str]],
         stop_sequences: Optional[List[str]] = None,
         grammar: Optional[str] = None,
-        max_tokens: int = 1500,
         tools_to_call_from: Optional[List[Tool]] = None,
     ) -> ChatMessage:
         messages = get_clean_message_list(
@@ -566,8 +603,6 @@ class OpenAIServerModel(Model):
                 tools=[get_json_schema(tool) for tool in tools_to_call_from],
                 tool_choice="auto",
                 stop=stop_sequences,
-                max_tokens=max_tokens,
-                temperature=self.temperature,
                 **self.kwargs,
             )
         else:
@@ -575,8 +610,6 @@ class OpenAIServerModel(Model):
                 model=self.model_id,
                 messages=messages,
                 stop=stop_sequences,
-                max_tokens=max_tokens,
-                temperature=self.temperature,
                 **self.kwargs,
             )
         self.last_input_token_count = response.usage.prompt_tokens


Test patch:
diff --git a/tests/test_agents.py b/tests/test_agents.py
index 1cd0a67..4a03137 100644
--- a/tests/test_agents.py
+++ b/tests/test_agents.py
@@ -35,6 +35,7 @@ from smolagents.models import (
     ChatMessageToolCall,
     ChatMessageToolCallDefinition,
 )
+from smolagents.utils import BASE_BUILTIN_MODULES
 
 
 def get_new_path(suffix="") -> str:
@@ -381,6 +382,12 @@ class AgentTests(unittest.TestCase):
         assert tool.name in agent.system_prompt
         assert tool.description in agent.system_prompt
 
+    def test_module_imports_get_baked_in_system_prompt(self):
+        agent = CodeAgent(tools=[], model=fake_code_model)
+        agent.run("Empty task")
+        for module in BASE_BUILTIN_MODULES:
+            assert module in agent.system_prompt
+
     def test_init_agent_with_different_toolsets(self):
         toolset_1 = []
         agent = CodeAgent(tools=toolset_1, model=fake_code_model)
diff --git a/tests/test_models.py b/tests/test_models.py
index 5a3a821..9921631 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -16,7 +16,7 @@ import unittest
 import json
 from typing import Optional
 
-from smolagents import models, tool, ChatMessage, HfApiModel
+from smolagents import models, tool, ChatMessage, HfApiModel, TransformersModel
 
 
 class ModelTests(unittest.TestCase):
@@ -46,6 +46,17 @@ class ModelTests(unittest.TestCase):
         assert data["content"] == "Hello!"
 
     def test_get_hfapi_message_no_tool(self):
-        model = HfApiModel()
+        model = HfApiModel(max_tokens=10)
         messages = [{"role": "user", "content": "Hello!"}]
         model(messages, stop_sequences=["great"])
+
+    def test_transformers_message_no_tool(self):
+        model = TransformersModel(
+            model_id="HuggingFaceTB/SmolLM2-135M-Instruct",
+            max_new_tokens=5,
+            device_map="auto",
+            do_sample=False,
+        )
+        messages = [{"role": "user", "content": "Hello!"}]
+        output = model(messages, stop_sequences=["great"]).content
+        assert output == "assistant\nHello"
diff --git a/tests/test_python_interpreter.py b/tests/test_python_interpreter.py
index 6e5b3c2..4976c56 100644
--- a/tests/test_python_interpreter.py
+++ b/tests/test_python_interpreter.py
@@ -920,3 +920,19 @@ shift_intervals
     Expected: {expected}
     Got:      {result}
     """
+
+    def test_dangerous_subpackage_access_blocked(self):
+        # Direct imports with dangerous patterns should fail
+        code = "import random._os"
+        with pytest.raises(InterpreterError):
+            evaluate_python_code(code)
+
+        # Import of whitelisted modules should succeed but dangerous submodules should not exist
+        code = "import random;random._os.system('echo bad command passed')"
+        with pytest.raises(AttributeError) as e:
+            evaluate_python_code(code)
+        assert "module 'random' has no attribute '_os'" in str(e)
+
+        code = "import doctest;doctest.inspect.os.system('echo bad command passed')"
+        with pytest.raises(AttributeError):
+            evaluate_python_code(code, authorized_imports=["doctest"])