Skip to content
Open
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
10 changes: 4 additions & 6 deletions src/smolagents/remote_executors.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,9 @@ def __init__(self, value):
def _deserialize_final_answer(encoded_value: str, allow_pickle: bool = True) -> Any:
"""Deserialize final answer with format detection.

Handle both new prefix-based format ("safe:" or "pickle:") and
legacy no-prefix base64-pickle format for backward compatibility.
Accepts explicit prefix-based formats only:
- "safe:" for JSON-safe payloads
- "pickle:" for pickle payloads (only when allow_pickle=True)

Args:
encoded_value: Serialized string from FinalAnswerException.
Expand All @@ -312,10 +313,7 @@ def _deserialize_final_answer(encoded_value: str, allow_pickle: bool = True) ->
raise SerializationError("Pickle data rejected: allow_pickle=False")
return pickle.loads(base64.b64decode(encoded_value[7:]))
else:
# Legacy format (no prefix) - base64-encoded pickle
if not allow_pickle:
raise SerializationError("Pickle data rejected: allow_pickle=False")
return pickle.loads(base64.b64decode(encoded_value))
raise SerializationError("Unknown final answer format: expected 'safe:' or 'pickle:' prefix")


class E2BExecutor(RemotePythonExecutor):
Expand Down
10 changes: 4 additions & 6 deletions tests/test_remote_executors.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import base64
import importlib
import io
import pickle
from textwrap import dedent
from unittest.mock import MagicMock, patch

Expand All @@ -21,6 +19,7 @@
RemotePythonExecutor,
WasmExecutor,
)
from smolagents.serialization import SerializationError
from smolagents.utils import AgentError

from .utils.markers import require_run_all
Expand Down Expand Up @@ -74,10 +73,9 @@ def test_send_variables_allow_pickle_handles_prefixed_payload(self):
assert isinstance(remote_scope["error"], ValueError)
assert str(remote_scope["error"]) == "boom"

def test_deserialize_final_answer_supports_legacy_no_prefix_pickle(self):
legacy_payload = base64.b64encode(pickle.dumps({"status": "ok", "count": 2})).decode()
result = RemotePythonExecutor._deserialize_final_answer(legacy_payload, allow_pickle=True)
assert result == {"status": "ok", "count": 2}
def test_deserialize_final_answer_rejects_unprefixed_payload(self):
with pytest.raises(SerializationError, match="Unknown final answer format"):
RemotePythonExecutor._deserialize_final_answer("legacy-unprefixed-payload", allow_pickle=True)

@require_run_all
def test_send_tools_with_default_wikipedia_search_tool(self):
Expand Down