Skip to content

Implement as langchain codeinterpreter sandbox provider#318

Open
FAUST-BENCHOU wants to merge 5 commits into
volcano-sh:mainfrom
FAUST-BENCHOU:feat/langchain
Open

Implement as langchain codeinterpreter sandbox provider#318
FAUST-BENCHOU wants to merge 5 commits into
volcano-sh:mainfrom
FAUST-BENCHOU:feat/langchain

Conversation

@FAUST-BENCHOU
Copy link
Copy Markdown
Contributor

What type of PR is this?

What this PR does / why we need it:

Which issue(s) this PR fixes:
Fixes #278

Special notes for your reviewer:

Does this PR introduce a user-facing change?:


Signed-off-by: zhoujinyu <2319109590@qq.com>
Signed-off-by: zhoujinyu <2319109590@qq.com>
Signed-off-by: zhoujinyu <2319109590@qq.com>
Signed-off-by: zhoujinyu <2319109590@qq.com>
Copilot AI review requested due to automatic review settings May 11, 2026 13:49
@volcano-sh-bot
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign hzxuzhonghu for approval. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@FAUST-BENCHOU FAUST-BENCHOU changed the title Implement as langchain codeinterpreter sandbox provider wip: Implement as langchain codeinterpreter sandbox provider May 11, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a LangChain integration for AgentCube, implementing a BaseSandbox backend for Deep Agents along with supporting examples and E2E tests. The core SDK is updated with a new execute_command_result method to handle command execution without raising exceptions on non-zero exit codes. Review feedback suggests avoiding hardcoded model versions in examples, refining output formatting when stdout is empty, ensuring robust handling of potential null values for exit codes, and maintaining consistent type hints across the SDK.

from langchain_anthropic import ChatAnthropic

return ChatAnthropic(
model=os.environ.get("ANTHROPIC_MODEL", "claude-haiku-4-5-20251001"),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Hardcoding a specific dated model version like claude-haiku-4-5-20251001 as a default can lead to maintenance issues when the model is deprecated or updated. Consider using a more stable alias (e.g., claude-3-5-haiku-latest) or requiring the user to provide the model via environment variables without a hardcoded default.

out = r.get("stdout") or ""
stderr = (r.get("stderr") or "").strip()
if stderr:
out += f"\n<stderr>{stderr}</stderr>"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Appending stderr with a leading newline can result in unexpected output formatting if stdout is empty (the result will start with a newline). Consider checking if out is empty before adding the newline.

Suggested change
out += f"\n<stderr>{stderr}</stderr>"
out = f"{out}\n<stderr>{stderr}</stderr>" if out else f"<stderr>{stderr}</stderr>"

return {
"stdout": result.get("stdout") or "",
"stderr": result.get("stderr") or "",
"exit_code": int(result.get("exit_code", -1)),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The use of result.get("exit_code", -1) will return None if the key "exit_code" exists in the response but its value is null. Passing None to int() will raise a TypeError. It is safer to handle the None case explicitly to avoid potential crashes if the API returns a null exit code.

Suggested change
"exit_code": int(result.get("exit_code", -1)),
"exit_code": int(result.get("exit_code") if result.get("exit_code") is not None else -1),

return self.dp_client.execute_command(command, timeout)

def execute_command_result(
self, command: str, timeout: Optional[float] = None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The type hint for command should be updated to str | list[str] to be consistent with the underlying CodeInterpreterDataPlaneClient.execute_command_result method which accepts both types. This ensures better type safety and documentation for users of the SDK.

Suggested change
self, command: str, timeout: Optional[float] = None
self, command: str | list[str], timeout: Optional[float] = None

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a LangChain Deep Agents sandbox backend for AgentCube’s Code Interpreter by introducing a new langchain-agentcube integration package and extending the Python SDK with a non-raising command execution API that the sandbox can use.

Changes:

  • Added langchain-agentcube integration implementing AgentcubeSandbox (Deep Agents BaseSandbox) with execute + file upload/download support.
  • Extended the Python SDK with execute_command_result() to return {stdout, stderr, exit_code} without raising on non-zero exit codes.
  • Wired the new integration into E2E runs (new E2E test + install step) and ensured CI sets up Python 3.11.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/e2e/test_langchain_agentcube_sandbox.py New E2E coverage for the Deep Agents sandbox adapter against live Router/WM.
test/e2e/run_e2e.sh Installs the new integration and runs the new LangChain sandbox E2E test.
sdk-python/agentcube/code_interpreter.py Exposes execute_command_result() on the high-level client.
sdk-python/agentcube/clients/code_interpreter_data_plane.py Implements execute_command_result() and refactors execute_command() to use it.
integrations/langchain-agentcube/README.md Usage docs for the new integration and example script.
integrations/langchain-agentcube/pyproject.toml New Python package metadata/deps for the integration.
integrations/langchain-agentcube/langchain_agentcube/sandbox.py Implements AgentcubeSandbox (execute + upload/download) on top of the SDK client.
integrations/langchain-agentcube/langchain_agentcube/init.py Exports AgentcubeSandbox.
integrations/langchain-agentcube/example/deep_agent_sandbox.py Example of using create_deep_agent(..., backend=AgentcubeSandbox(...)).
.github/workflows/e2e.yml Ensures Python 3.11 is available for E2E runs.

verbose=True,
) as client:
backend = AgentcubeSandbox(client=client)
self.assertTrue(backend.id)

def _normalize_remote_path(path: str) -> str:
"""Map paths from Deep Agents (often absolute) to session workspace-relative paths."""
return path.replace("\\", "/").strip().lstrip("/")
@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 11, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 47.74%. Comparing base (524e55e) to head (2ac342f).
⚠️ Report is 41 commits behind head on main.
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #318      +/-   ##
==========================================
+ Coverage   47.57%   47.74%   +0.17%     
==========================================
  Files          30       30              
  Lines        2819     2855      +36     
==========================================
+ Hits         1341     1363      +22     
- Misses       1338     1344       +6     
- Partials      140      148       +8     
Flag Coverage Δ
unittests 47.74% <ø> (+0.17%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@FAUST-BENCHOU
Copy link
Copy Markdown
Contributor Author

record for result

(base) zhoujinyu@zhoujinyudeMacBook-Air agentcube % python integrations/langchain-agentcube/example/deep_agent_sandbox.py \
  --interpreter my-interpreter \
  --namespace default
/Users/zhoujinyu/miniconda3/lib/python3.12/site-packages/requests/__init__.py:113: RequestsDependencyWarning: urllib3 (2.3.0) or chardet (7.4.2)/charset_normalizer (3.4.4) doesn't match a supported version!
  warnings.warn(
/Users/zhoujinyu/miniconda3/lib/python3.12/site-packages/langgraph/checkpoint/serde/encrypted.py:5: LangChainPendingDeprecationWarning: The default value of `allowed_objects` will change in a future version. Pass an explicit value (e.g., allowed_objects='messages' or allowed_objects='core') to suppress this warning.
  from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
2026-05-11 21:33:53,219 | INFO | agentcube.code_interpreter | Creating new session...
2026-05-11 21:33:53,294 | INFO | agentcube.code_interpreter | Session created: a984f18c-7c79-4293-806e-6156dc761643
2026-05-11 21:34:09,713 | INFO | agentcube.code_interpreter | Deleting session a984f18c-7c79-4293-806e-6156dc761643...
{'messages': [HumanMessage(content="Write and run a Python script to print 'Hello, World!'", additional_kwargs={}, response_metadata={}, id='3dfab715-9ecb-4304-bce8-3ea00dbbaf08'), AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 71, 'prompt_tokens': 6679, 'total_tokens': 6750, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 6656}, 'prompt_cache_hit_tokens': 6656, 'prompt_cache_miss_tokens': 23}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '5d6b4c8d-2185-4951-9f8d-18660cd8565e', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e173e-838f-7292-a0ab-5e3914fa39f2-0', tool_calls=[{'name': 'write_file', 'args': {'file_path': '/home/user/hello.py', 'content': "print('Hello, World!')\n"}, 'id': 'call_00_VHGrWo2uaEevkVFshH0O3332', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 6679, 'output_tokens': 71, 'total_tokens': 6750, 'input_token_details': {'cache_read': 6656}, 'output_token_details': {}}), ToolMessage(content='Updated file /home/user/hello.py', name='write_file', id='58141ccc-6413-4a92-a1d7-bb9df6b08996', tool_call_id='call_00_VHGrWo2uaEevkVFshH0O3332'), AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 48, 'prompt_tokens': 6770, 'total_tokens': 6818, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 6656}, 'prompt_cache_hit_tokens': 6656, 'prompt_cache_miss_tokens': 114}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '95ed2f96-5f02-4a9a-8322-48ab070f2c88', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e173e-89a0-7000-9bb9-02ae84c35211-0', tool_calls=[{'name': 'execute', 'args': {'command': 'python /home/user/hello.py'}, 'id': 'call_00_Fx0UvDKqrtyFjlIg98w92687', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 6770, 'output_tokens': 48, 'total_tokens': 6818, 'input_token_details': {'cache_read': 6656}, 'output_token_details': {}}), ToolMessage(content='\n<stderr>exec: "python": executable file not found in $PATH</stderr>\n[Command failed with exit code 1]', name='execute', id='f1488484-5bc6-4944-95fa-3715d58f2573', tool_call_id='call_00_Fx0UvDKqrtyFjlIg98w92687'), AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 49, 'prompt_tokens': 6859, 'total_tokens': 6908, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 6784}, 'prompt_cache_hit_tokens': 6784, 'prompt_cache_miss_tokens': 75}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '8c3f876b-2359-4fce-8f58-d16bfc84d294', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e173e-8e06-7092-9432-ee69a78fdb62-0', tool_calls=[{'name': 'execute', 'args': {'command': 'python3 /home/user/hello.py'}, 'id': 'call_00_yUZ8e6JZ86tWyn94RkKV1637', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 6859, 'output_tokens': 49, 'total_tokens': 6908, 'input_token_details': {'cache_read': 6784}, 'output_token_details': {}}), ToolMessage(content="\n<stderr>python3: can't open file '/home/user/hello.py': [Errno 2] No such file or directory</stderr>\n[Command failed with exit code 2]", name='execute', id='6d5bdc57-3d4c-45aa-a5ab-30cf6b0a865f', tool_call_id='call_00_yUZ8e6JZ86tWyn94RkKV1637'), AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 58, 'prompt_tokens': 6962, 'total_tokens': 7020, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 6784}, 'prompt_cache_hit_tokens': 6784, 'prompt_cache_miss_tokens': 178}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': 'f54ef890-fe39-44cf-af11-f195071f88f2', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e173e-929d-7382-a171-93932352d0a8-0', tool_calls=[{'name': 'execute', 'args': {'command': 'which python3 python python2 2>/dev/null; ls /home/user/'}, 'id': 'call_00_GWWSa0Gyp0vQU4V2pIND5158', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 6962, 'output_tokens': 58, 'total_tokens': 7020, 'input_token_details': {'cache_read': 6784}, 'output_token_details': {}}), ToolMessage(content='/usr/bin/python3\n/usr/bin/ls\n\n[Command failed with exit code 1]', name='execute', id='a8c82aea-63ff-4a7f-bd2d-848d8d69e715', tool_call_id='call_00_GWWSa0Gyp0vQU4V2pIND5158'), AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 52, 'prompt_tokens': 7052, 'total_tokens': 7104, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 6912}, 'prompt_cache_hit_tokens': 6912, 'prompt_cache_miss_tokens': 140}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '6ec86816-7d7d-4702-b4e6-67a3a9dc2fef', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e173e-98e8-72b1-bc67-eda27ec8b957-0', tool_calls=[{'name': 'execute', 'args': {'command': '/usr/bin/python3 /home/user/hello.py'}, 'id': 'call_00_FALqN0hzl67UcxdagJN82151', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 7052, 'output_tokens': 52, 'total_tokens': 7104, 'input_token_details': {'cache_read': 6912}, 'output_token_details': {}}), ToolMessage(content="\n<stderr>/usr/bin/python3: can't open file '/home/user/hello.py': [Errno 2] No such file or directory</stderr>\n[Command failed with exit code 2]", name='execute', id='db6db3f9-4f8c-48ad-8550-9419e62dc54f', tool_call_id='call_00_FALqN0hzl67UcxdagJN82151'), AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 49, 'prompt_tokens': 7160, 'total_tokens': 7209, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 7040}, 'prompt_cache_hit_tokens': 7040, 'prompt_cache_miss_tokens': 120}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': 'bd84b86f-500d-4305-9dc6-88821a4236ae', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e173e-a0ee-76f2-9ea5-93d2bf02a471-0', tool_calls=[{'name': 'read_file', 'args': {'file_path': '/home/user/hello.py'}, 'id': 'call_00_4uVjAu6CuOC6pyFdRD8P7130', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 7160, 'output_tokens': 49, 'total_tokens': 7209, 'input_token_details': {'cache_read': 7040}, 'output_token_details': {}}), ToolMessage(content="Error: File '/home/user/hello.py': file_not_found", name='read_file', id='7898ccea-60f3-4e59-8018-917a337886a3', tool_call_id='call_00_4uVjAu6CuOC6pyFdRD8P7130', status='error'), AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 44, 'prompt_tokens': 7235, 'total_tokens': 7279, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 7168}, 'prompt_cache_hit_tokens': 7168, 'prompt_cache_miss_tokens': 67}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '28af7892-adac-4231-b0b8-cd4523cdd481', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e173e-a4bd-7a22-942c-65f912e41f23-0', tool_calls=[{'name': 'ls', 'args': {'path': '/home/user'}, 'id': 'call_00_Z8HBp07vaIFWlSESiWMh7551', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 7235, 'output_tokens': 44, 'total_tokens': 7279, 'input_token_details': {'cache_read': 7168}, 'output_token_details': {}}), ToolMessage(content='[]', name='ls', id='41c1c43f-a2af-4340-88e5-c6fb59187eec', tool_call_id='call_00_Z8HBp07vaIFWlSESiWMh7551'), AIMessage(content="The file write succeeded according to the tool but the file doesn't exist on disk. Let me try again.", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 91, 'prompt_tokens': 7292, 'total_tokens': 7383, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 7168}, 'prompt_cache_hit_tokens': 7168, 'prompt_cache_miss_tokens': 124}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '830a750c-02c0-4d83-bdc3-82bc9d8511ef', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e173e-aab0-7101-b2ab-fa334764f86c-0', tool_calls=[{'name': 'execute', 'args': {'command': 'echo "print(\'Hello, World!\')" > /home/user/hello.py && /usr/bin/python3 /home/user/hello.py'}, 'id': 'call_00_b6zXWeQY94BUEgwAuBhw7736', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 7292, 'output_tokens': 91, 'total_tokens': 7383, 'input_token_details': {'cache_read': 7168}, 'output_token_details': {}}), ToolMessage(content="print('Hello, World!') > /home/user/hello.py && /usr/bin/python3 /home/user/hello.py\n\n[Command succeeded with exit code 0]", name='execute', id='3d338f4b-2d11-4377-9244-0a71e87e43f4', tool_call_id='call_00_b6zXWeQY94BUEgwAuBhw7736'), AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 52, 'prompt_tokens': 7431, 'total_tokens': 7483, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 7296}, 'prompt_cache_hit_tokens': 7296, 'prompt_cache_miss_tokens': 135}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '4febb16d-b22e-4e8e-86cf-3a7c07afc804', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e173e-b1d0-7d92-b7c6-a7f194b3456c-0', tool_calls=[{'name': 'execute', 'args': {'command': '/usr/bin/python3 /home/user/hello.py'}, 'id': 'call_00_57L3GuX54raAaxcNfmGg0521', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 7431, 'output_tokens': 52, 'total_tokens': 7483, 'input_token_details': {'cache_read': 7296}, 'output_token_details': {}}), ToolMessage(content="\n<stderr>/usr/bin/python3: can't open file '/home/user/hello.py': [Errno 2] No such file or directory</stderr>\n[Command failed with exit code 2]", name='execute', id='b8a7f366-ffba-488f-bb40-36b7654f180b', tool_call_id='call_00_57L3GuX54raAaxcNfmGg0521'), AIMessage(content="The sandbox filesystem seems ephemeral — files written via `write_file` or shell redirection don't persist across commands. Let me try running the script inline.", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 90, 'prompt_tokens': 7539, 'total_tokens': 7629, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 7424}, 'prompt_cache_hit_tokens': 7424, 'prompt_cache_miss_tokens': 115}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '932ee21b-02bc-4440-99f3-0366f9e68eec', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e173e-b723-7080-bbd2-286c637e0c85-0', tool_calls=[{'name': 'execute', 'args': {'command': '/usr/bin/python3 -c "print(\'Hello, World!\')"'}, 'id': 'call_00_tFOUb05ka0ZceDwJQbSo1515', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 7539, 'output_tokens': 90, 'total_tokens': 7629, 'input_token_details': {'cache_read': 7424}, 'output_token_details': {}}), ToolMessage(content='Hello, World!\n\n[Command succeeded with exit code 0]', name='execute', id='b1725162-db7a-4ec5-bc21-f90820549517', tool_call_id='call_00_tFOUb05ka0ZceDwJQbSo1515'), AIMessage(content="**Hello, World!** printed successfully. The sandbox filesystem doesn't persist files between commands, so I ran the script inline with `python3 -c`.", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 34, 'prompt_tokens': 7654, 'total_tokens': 7688, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 7552}, 'prompt_cache_hit_tokens': 7552, 'prompt_cache_miss_tokens': 102}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': 'f2193cec-bb54-43a0-9c12-937b2bd4fd08', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019e173e-be62-7b70-87e5-129c46d13152-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 7654, 'output_tokens': 34, 'total_tokens': 7688, 'input_token_details': {'cache_read': 7552}, 'output_token_details': {}})]}
  1. HumanMessage
    User task: Write and run Python, printing "Hello, World!".

  2. AIMessage (empty content, with tool_calls)
    The model decides to call the write_file tool to write /home/user/hello.py in the sandbox.

  3. ToolMessage (write_file)
    The tool returns: The path has been updated (Deep Agents consider the write successful).

  4. AIMessage (tool_calls → execute)
    The model uses execute to run python /home/user/hello.py.

  5. ToolMessage (execute)
    Failure: Python is not found in the image.

  6. AIMessage → execute
    Change to python3 /home/user/hello.py.

  7. ToolMessage
    Failure: No such file or directory—This indicates that the path written by write_file does not match the file system seen in execute (not "not persistent", but a typical manifestation of inconsistency between two views/root directories).

  8. AIMessage → execute
    Environment detection: which python3 ...; ls /home/user/.

  9. ToolMessage
    Supposes /usr/bin/python3 exists, but the ls part of the command or the pipe causes a non-zero exit code (so it's still marked as command failed, but the path is visible in stdout).

  10. AIMessage → execute
    Using the absolute path /usr/bin/python3 /home/user/hello.py.

  11. ToolMessage
    Still cannot find hello.py.

  12. AIMessage → read_file
    Reads /home/user/hello.py directly.

  13. ToolMessage
    file_not_found.

  14. AIMessage → ls
    Lists /home/user.

  15. ToolMessage
    Returns [] (This directory is empty from the execute perspective).

  16. AIMessage (with natural language + execute)
    The model interprets "ToolMessage says it's written but not on disk," so it uses shell instead: echo ... > ... && python3 ....

  17. ToolMessage
    Exit code 0, but the output seems to be redirected incorrectly (the entire echo line is treated as normal execution, leaving the model confused).

  18. AIMessage → execute
    Try /usr/bin/python3 /home/user/hello.py again.

  19. ToolMessage
    Still no file found.

  20. AIMessage (natural language + execute)
    The model misinterprets this as "non-persistent across commands," so it uses python3 -c for inline execution, which doesn't depend on the file.

  21. ToolMessage
    Success: Hello, World! + exit 0.

  22. Last AIMessage (finish_reason: stop, with body) Model summary: Successfully printed, and explained that python3 -c was used (the conclusion about "files not persistent" is not entirely accurate; the root cause is more likely the inconsistency between write_file and execute working directory/mount).

@FAUST-BENCHOU FAUST-BENCHOU changed the title wip: Implement as langchain codeinterpreter sandbox provider Implement as langchain codeinterpreter sandbox provider May 11, 2026
@FAUST-BENCHOU
Copy link
Copy Markdown
Contributor Author

It's unclear why Deepseek keeps getting hung up on the path issue and insists on using absolute paths (which would definitely fail due to the existence of picod). However, LangChain itself is functioning correctly, and Deepagent can plan normally.

@FAUST-BENCHOU
Copy link
Copy Markdown
Contributor Author

FAUST-BENCHOU commented May 12, 2026

/cc @hzxuzhonghu @acsoto

Signed-off-by: zhoujinyu <2319109590@qq.com>
@hzxuzhonghu
Copy link
Copy Markdown
Member

It's unclear why Deepseek keeps getting hung up on the path issue and insists on using absolute paths

What does this mean? While deepagent works well

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am thinking about contributing to upstream lang chain organization if possible

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

about what. The agentcube Code Interpreter?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implement, you can see aws agentcore is there.

@hzxuzhonghu
Copy link
Copy Markdown
Member

Ah, you mean it cannot find hello.py, if you still have the env, mind kubectl exec and find it?

@FAUST-BENCHOU
Copy link
Copy Markdown
Contributor Author

Ah, you mean it cannot find hello.py, if you still have the env, mind kubectl exec and find it?

exactly,The model interprets "hello.py in the working directory" as /hello.py (disk root), while the actual file is located in {working directory}/hello.py (e.g., /root/hello.py).
I'll test later on

@FAUST-BENCHOU
Copy link
Copy Markdown
Contributor Author

(base) zhoujinyu@zhoujinyudeMacBook-Air agentcube % python integrations/langchain-agentcube/example/deep_agent_sandbox.py \
  --trace-workspace --keep-session 2>&1 | tee trace.log
/Users/zhoujinyu/miniconda3/lib/python3.12/site-packages/requests/__init__.py:113: RequestsDependencyWarning: urllib3 (2.3.0) or chardet (7.4.2)/charset_normalizer (3.4.4) doesn't match a supported version!
  warnings.warn(
/Users/zhoujinyu/miniconda3/lib/python3.12/site-packages/langgraph/checkpoint/serde/encrypted.py:5: LangChainPendingDeprecationWarning: The default value of `allowed_objects` will change in a future version. Pass an explicit value (e.g., allowed_objects='messages' or allowed_objects='core') to suppress this warning.
  from langgraph.checkpoint.serde.jsonplus import JsonPlusSerializer
[deep_agent_sandbox] 正在调用模型 workspace 跟踪在 stderr。
2026-05-13 13:08:17,937 | INFO | agentcube.code_interpreter | Creating new session...
2026-05-13 13:08:18,053 | INFO | agentcube.code_interpreter | Session created: 5d37c687-d82d-4e91-b354-75499fa85f0f
[deep_agent_sandbox] session_id=5d37c687-d82d-4e91-b354-75499fa85f0f
[deep_agent_sandbox] 正在 create_deep_agent)…
[deep_agent_sandbox] agent.invoke 开始…
[deep_agent_sandbox] agent.invoke 结束。
[deep_agent_sandbox] --- 远程工作区(Router 同 session)---
=== pwd ===
/root
=== ls -la ===
total 8348
drwx------ 1 root root    4096 May 13 05:08 .
drwxr-xr-x 1 root root    4096 May 13 03:24 ..
-rw-r--r-- 1 root root    3106 Apr 22  2024 .bashrc
-rw-r--r-- 1 root root     161 Apr 22  2024 .profile
drwxr-xr-x 3 root root    4096 May 13 05:08 home
-rwxr-xr-x 1 root root 8519864 Apr 20 04:29 picod
drwxr-xr-x 2 root root    4096 May 13 05:08 tmp
=== find . (maxdepth 6, head 500) ===
.
./.profile
./.bashrc
./tmp
./tmp/run_hello.py
./tmp/hello.py
./home
./home/user
./home/user/hello.py
./picod
[keep-session] Session was NOT deleted; inspect the sandbox pod while it is still bound (session_id='5d37c687-d82d-4e91-b354-75499fa85f0f'). It will expire per cluster TTL.
{'messages': [HumanMessage(content="Write and run a Python script to print 'Hello, World!'", additional_kwargs={}, response_metadata={}, id='9c727caa-866e-46e7-9865-bced15bf63b0'), AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 71, 'prompt_tokens': 5860, 'total_tokens': 5931, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 5760}, 'prompt_cache_hit_tokens': 5760, 'prompt_cache_miss_tokens': 100}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '51b800af-4ebc-4af2-87b8-ecea34c95195', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e1fbc-5a95-7f33-bb7e-963844a114f4-0', tool_calls=[{'name': 'write_file', 'args': {'file_path': '/home/user/hello.py', 'content': "print('Hello, World!')\n"}, 'id': 'call_00_UXdUfwBx2AvlmxXsEG9o2071', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 5860, 'output_tokens': 71, 'total_tokens': 5931, 'input_token_details': {'cache_read': 5760}, 'output_token_details': {}}), ToolMessage(content='Updated file /home/user/hello.py', name='write_file', id='21ea5f2b-074a-48a9-9eb1-52a8158dbec6', tool_call_id='call_00_UXdUfwBx2AvlmxXsEG9o2071'), AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 85, 'prompt_tokens': 5951, 'total_tokens': 6036, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 5888}, 'prompt_cache_hit_tokens': 5888, 'prompt_cache_miss_tokens': 63}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '26e268b2-9d4e-4921-83a0-73e3e32dc93d', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e1fbc-5f06-7eb1-a7cc-b7083fb6b1e0-0', tool_calls=[{'name': 'task', 'args': {'description': 'Run the Python script at /home/user/hello.py using the `python3` command. Return the output of the script.', 'subagent_type': 'general-purpose'}, 'id': 'call_00_iRwuC9TSB2oLukOLg6w31196', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 5951, 'output_tokens': 85, 'total_tokens': 6036, 'input_token_details': {'cache_read': 5888}, 'output_token_details': {}}), ToolMessage(content="The `/home/user/` directory exists but is empty — there is no `hello.py` file there. I cannot run a script that doesn't exist. Could you please check if the file might be located elsewhere, or would you like me to create the `hello.py` script first?", name='task', id='cc95048d-bc44-41f2-801f-e9dd75596623', tool_call_id='call_00_iRwuC9TSB2oLukOLg6w31196'), AIMessage(content="The file was created but the subagent couldn't find it. Let me run it directly.", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 62, 'prompt_tokens': 6105, 'total_tokens': 6167, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 6016}, 'prompt_cache_hit_tokens': 6016, 'prompt_cache_miss_tokens': 89}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '4909e12d-2cc5-4ba4-90b5-a3620d588f90', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e1fbc-6eef-7d52-bddb-994113c297c0-0', tool_calls=[{'name': 'ls', 'args': {'path': '/home/user'}, 'id': 'call_00_jugKIvatvci9PItwK9HP4980', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 6105, 'output_tokens': 62, 'total_tokens': 6167, 'input_token_details': {'cache_read': 6016}, 'output_token_details': {}}), ToolMessage(content='[]', name='ls', id='339548b3-ae7a-4e4f-abd1-f3ddf9b6b00a', tool_call_id='call_00_jugKIvatvci9PItwK9HP4980'), AIMessage(content='The file write seems to have not persisted. Let me create and run it in one step.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 89, 'prompt_tokens': 6180, 'total_tokens': 6269, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 6144}, 'prompt_cache_hit_tokens': 6144, 'prompt_cache_miss_tokens': 36}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '33fb94b9-8798-4965-b11f-ffcee5e700d3', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e1fbc-72fb-74a2-a90a-4fd686215426-0', tool_calls=[{'name': 'write_file', 'args': {'file_path': '/home/user/hello.py', 'content': "print('Hello, World!')\n"}, 'id': 'call_00_mCLA91N15G2Z0qGJstje7455', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 6180, 'output_tokens': 89, 'total_tokens': 6269, 'input_token_details': {'cache_read': 6144}, 'output_token_details': {}}), ToolMessage(content='Updated file /home/user/hello.py', name='write_file', id='30e7f1a2-f30a-4724-b4dc-46630243cda7', tool_call_id='call_00_mCLA91N15G2Z0qGJstje7455'), AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 44, 'prompt_tokens': 6289, 'total_tokens': 6333, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 6144}, 'prompt_cache_hit_tokens': 6144, 'prompt_cache_miss_tokens': 145}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '560aed99-1313-4e04-a370-1282b108a9df', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e1fbc-7918-7451-b80b-26fbbd73ed49-0', tool_calls=[{'name': 'ls', 'args': {'path': '/home/user'}, 'id': 'call_00_7HMjb163WGmM3Iayz6lx3335', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 6289, 'output_tokens': 44, 'total_tokens': 6333, 'input_token_details': {'cache_read': 6144}, 'output_token_details': {}}), ToolMessage(content='[]', name='ls', id='36036a54-e9b8-440d-96a1-be1a1ac25ae8', tool_call_id='call_00_7HMjb163WGmM3Iayz6lx3335'), AIMessage(content="It seems the filesystem doesn't persist files. Let me try running the script directly using Python inline.", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 105, 'prompt_tokens': 6346, 'total_tokens': 6451, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 6272}, 'prompt_cache_hit_tokens': 6272, 'prompt_cache_miss_tokens': 74}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': '1c0ce9d3-3374-4842-9468-b68ca44c0da1', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019e1fbc-7c72-7ca0-88c2-5be81b03841e-0', tool_calls=[{'name': 'task', 'args': {'description': 'Run the following Python command and return the output:\n```\npython3 -c "print(\'Hello, World!\')"\n```', 'subagent_type': 'general-purpose'}, 'id': 'call_00_VFLholdVVuO8BUdlkJHF8666', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 6346, 'output_tokens': 105, 'total_tokens': 6451, 'input_token_details': {'cache_read': 6272}, 'output_token_details': {}}), ToolMessage(content='**Output:**\n```\nHello, World!\n```', name='task', id='70705445-78d0-43f8-b721-9eedab380ebb', tool_call_id='call_00_VFLholdVVuO8BUdlkJHF8666'), AIMessage(content="The script ran successfully. Output:\n\n```\nHello, World!\n```\n\nNote: The filesystem in this sandbox doesn't persist files between tool calls, so the script was executed inline instead. The Python code `print('Hello, World!')` executed correctly.", additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 53, 'prompt_tokens': 6473, 'total_tokens': 6526, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 6400}, 'prompt_cache_hit_tokens': 6400, 'prompt_cache_miss_tokens': 73}, 'model_provider': 'openai', 'model_name': 'deepseek-v4-flash', 'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402', 'id': 'f2785481-8585-488b-a5d3-103cf40660db', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--019e1fbc-9d15-7e42-aa56-927fa90e3288-0', tool_calls=[], invalid_tool_calls=[], usage_metadata={'input_tokens': 6473, 'output_tokens': 53, 'total_tokens': 6526, 'input_token_details': {'cache_read': 6400}, 'output_token_details': {}})]}

it creats lots of hello.py in different places

./tmp/run_hello.py
./tmp/hello.py
./home/user/hello.py

@FAUST-BENCHOU
Copy link
Copy Markdown
Contributor Author

at last it give up creating file in workspace and run python3 -c "print('Hello, World!')" directly
/cc @hzxuzhonghu

@hzxuzhonghu
Copy link
Copy Markdown
Member

Seems the client want to upload the file in /home/user/hello.py, but picod write it in /{workspace}/home/user/hello.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement as langchain codeinterpreter sandbox provider

5 participants