Skip to content
Merged
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ OpenSandbox provides examples covering SDK usage, agent integrations, browser au
- **[claude-code](examples/claude-code/README.md)** - Run Claude Code inside OpenSandbox.
- **[gemini-cli](examples/gemini-cli/README.md)** - Run Google Gemini CLI inside OpenSandbox.
- **[codex-cli](examples/codex-cli/README.md)** - Run OpenAI Codex CLI inside OpenSandbox.
- **[qwen-code](examples/qwen-code/README.md)** - Run Qwen Code inside OpenSandbox.
- **[kimi-cli](examples/kimi-cli/README.md)** - Run [Kimi CLI](https://github.com/MoonshotAI/kimi-cli) (Moonshot AI) inside OpenSandbox.
- **[langgraph](examples/langgraph/README.md)** - LangGraph state-machine workflow that creates/runs a sandbox job with fallback retry.
- **[google-adk](examples/google-adk/README.md)** - Google ADK agent using OpenSandbox tools to write/read files and run commands.
Expand Down
3 changes: 2 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Examples for common OpenSandbox use cases. Each subdirectory contains runnable c
- <img src="https://img.shields.io/badge/-%20-D97757?logo=claude&logoColor=white&style=flat-square" alt="Claude" width="16" height="16" style="display:inline-block;width:16px;height:16px;vertical-align:middle;margin-right:4px;" /> [**claude-code**](claude-code): Call Claude (Anthropic) API/CLI within the sandbox
- <img src="https://geminicli.com/favicon.ico" alt="Google Gemini" width="16" height="16" style="display:inline-block;width:16px;height:16px;vertical-align:middle;margin-right:4px;" /> [**gemini-cli**](gemini-cli): Call Google Gemini within the sandbox
- <img src="https://developers.openai.com/favicon.png" alt="OpenAI" width="16" height="16" style="display:inline-block;width:16px;height:16px;vertical-align:middle;margin-right:4px;" /> [**codex-cli**](codex-cli): Call OpenAI/Codex-like models within the sandbox
- <img src="https://avatars.githubusercontent.com/u/159934110?s=32&v=4" alt="Qwen" width="16" height="16" style="display:inline-block;width:16px;height:16px;vertical-align:middle;margin-right:4px;" /> [**qwen-code**](qwen-code): Run Qwen Code inside the sandbox
- <img src="https://www.kimi.com/favicon.ico" alt="Kimi" width="16" height="16" style="display:inline-block;width:16px;height:16px;vertical-align:middle;margin-right:4px;" /> [**kimi-cli**](kimi-cli): Call Kimi Code CLI (Moonshot AI) within the sandbox
- <img src="https://img.shields.io/badge/-%20-1C3C3C?logo=langgraph&logoColor=white&style=flat-square" alt="LangGraph" width="16" height="16" style="display:inline-block;width:16px;height:16px;vertical-align:middle;margin-right:4px;" /> [**langgraph**](langgraph): LangGraph agent orchestrating sandbox lifecycle + tools
- <img src="https://google.github.io/adk-docs/assets/agent-development-kit.png" alt="Google ADK" width="16" height="16" style="display:inline-block;width:16px;height:16px;vertical-align:middle;margin-right:4px;" /> [**google-adk**](google-adk): Google ADK agent calling OpenSandbox tools
Expand All @@ -24,7 +25,7 @@ Examples for common OpenSandbox use cases. Each subdirectory contains runnable c

## How to Run
- Set basic environment variables (e.g., `export SANDBOX_DOMAIN=...`, `export SANDBOX_API_KEY=...`)
- Add provider-specific variables as needed (e.g., `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `KIMI_API_KEY`, etc.; model selection is optional)
- Add provider-specific variables as needed (e.g., `ANTHROPIC_AUTH_TOKEN`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `API_KEY` for Qwen Code, `KIMI_API_KEY`, etc.; model selection is optional)
- Navigate to the example directory and install dependencies: `pip install -r requirements.txt` (or refer to the Dockerfile in the directory)
- Then execute: `python main.py`
- To run in a container, build and run using the `Dockerfile` in the directory
Expand Down
53 changes: 53 additions & 0 deletions examples/qwen-code/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Qwen Code Example

Run [Qwen Code](https://github.com/QwenLM/qwen-code) inside an OpenSandbox container through an OpenAI-compatible endpoint.

## Start OpenSandbox server [local]

Pre-pull the code-interpreter image (includes Node.js):

```shell
docker pull sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/code-interpreter:v1.0.2

# use docker hub
# docker pull opensandbox/code-interpreter:v1.0.2
```

Then start the local OpenSandbox server, stdout logs will be visible in the terminal:

```shell
uv pip install opensandbox-server
opensandbox-server init-config ~/.sandbox.toml --example docker
opensandbox-server
```

## Create and Access the Qwen Sandbox

```shell
# Install OpenSandbox package
uv pip install opensandbox

# Export provider settings
export API_KEY=your-api-key
export BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
export MODEL_NAME=qwen3-coder-plus

# Run the example
uv run python examples/qwen-code/main.py
```

The script installs Qwen Code (`npm install -g @qwen-code/qwen-code@latest`) at runtime, writes a project-local `.qwen/settings.json` inside the sandbox, and runs `qwen -p "Compute 1+1 and reply with only the final number."` in headless mode. The API key is injected only through the `API_KEY` environment variable and is not written into the repository.

## Environment Variables

- `SANDBOX_DOMAIN`: Sandbox service address (default: `localhost:8080`)
- `SANDBOX_API_KEY`: API key if your server requires authentication (optional for local)
- `SANDBOX_IMAGE`: Sandbox image to use (default: `sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/code-interpreter:v1.0.2`)
- `API_KEY`: API key for the OpenAI-compatible provider used by Qwen Code (required)
- `BASE_URL`: OpenAI-compatible base URL (default: `https://dashscope.aliyuncs.com/compatible-mode/v1`)
- `MODEL_NAME`: Model name for Qwen Code (default: `qwen3-coder-plus`)

## References

- [Qwen Code](https://github.com/QwenLM/qwen-code) - Official repository
- [Qwen Code Authentication](https://qwenlm.github.io/qwen-code-docs/en/users/configuration/auth/) - Provider configuration reference
123 changes: 123 additions & 0 deletions examples/qwen-code/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Copyright 2025 Alibaba Group Holding Ltd.
#
# 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 asyncio
import json
import os
from datetime import timedelta

from opensandbox import Sandbox
from opensandbox.config import ConnectionConfig
from opensandbox.models.filesystem import WriteEntry


QWEN_PROJECT_DIR = "/tmp/qwen-code-example"
QWEN_SETTINGS_DIR = f"{QWEN_PROJECT_DIR}/.qwen"
QWEN_SETTINGS_PATH = f"{QWEN_SETTINGS_DIR}/settings.json"


def _required_env(name: str) -> str:
value = os.getenv(name)
if not value:
raise RuntimeError(f"{name} is required")
return value


def _build_qwen_settings(base_url: str, model_name: str) -> str:
settings = {
"modelProviders": {
"openai": [
{
"id": model_name,
"name": model_name,
"baseUrl": base_url,
"description": "Qwen Code via OpenAI-compatible API in OpenSandbox",
"envKey": "API_KEY",
}
]
},
"security": {
"auth": {
"selectedType": "openai",
}
},
"model": {
"name": model_name,
},
}
return json.dumps(settings, indent=2)


async def _print_execution_logs(execution) -> None:
for msg in execution.logs.stdout:
print(f"[stdout] {msg.text}")
for msg in execution.logs.stderr:
print(f"[stderr] {msg.text}")
if execution.error:
print(f"[error] {execution.error.name}: {execution.error.value}")


async def main() -> None:
domain = os.getenv("SANDBOX_DOMAIN", "localhost:8080")
api_key = os.getenv("SANDBOX_API_KEY")
qwen_api_key = _required_env("API_KEY")
qwen_base_url = os.getenv("BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1")
qwen_model_name = os.getenv("MODEL_NAME", "qwen3-coder-plus")
image = os.getenv(
"SANDBOX_IMAGE",
"sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/code-interpreter:v1.0.2",
)

config = ConnectionConfig(
domain=domain,
api_key=api_key,
request_timeout=timedelta(seconds=60),
)

sandbox = await Sandbox.create(
image,
connection_config=config,
env={"API_KEY": qwen_api_key},
)

async with sandbox:
await sandbox.files.create_directories(
[
WriteEntry(path=QWEN_PROJECT_DIR, mode=755),
WriteEntry(path=QWEN_SETTINGS_DIR, mode=755),
]
)
await sandbox.files.write_file(
QWEN_SETTINGS_PATH,
_build_qwen_settings(qwen_base_url, qwen_model_name),
mode=644,
)

# Install Qwen Code CLI (Node.js is already in the code-interpreter image).
install_exec = await sandbox.commands.run(
"npm install -g @qwen-code/qwen-code@latest"
)
await _print_execution_logs(install_exec)

# Run Qwen Code in headless mode using the project-local config.
run_exec = await sandbox.commands.run(
'cd /tmp/qwen-code-example && qwen -p "Compute 1+1 and reply with only the final number."'
)
await _print_execution_logs(run_exec)

await sandbox.kill()


if __name__ == "__main__":
asyncio.run(main())
Loading