main.py
1 # Copyright 2025 Alibaba Group Holding Ltd. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 import asyncio 16 import json 17 import os 18 from datetime import timedelta 19 20 from opensandbox import Sandbox 21 from opensandbox.config import ConnectionConfig 22 from opensandbox.models.filesystem import WriteEntry 23 24 25 QWEN_PROJECT_DIR = "/tmp/qwen-code-example" 26 QWEN_SETTINGS_DIR = f"{QWEN_PROJECT_DIR}/.qwen" 27 QWEN_SETTINGS_PATH = f"{QWEN_SETTINGS_DIR}/settings.json" 28 29 30 def _required_env(name: str) -> str: 31 value = os.getenv(name) 32 if not value: 33 raise RuntimeError(f"{name} is required") 34 return value 35 36 37 def _build_qwen_settings(base_url: str, model_name: str) -> str: 38 settings = { 39 "modelProviders": { 40 "openai": [ 41 { 42 "id": model_name, 43 "name": model_name, 44 "baseUrl": base_url, 45 "description": "Qwen Code via OpenAI-compatible API in OpenSandbox", 46 "envKey": "API_KEY", 47 } 48 ] 49 }, 50 "security": { 51 "auth": { 52 "selectedType": "openai", 53 } 54 }, 55 "model": { 56 "name": model_name, 57 }, 58 } 59 return json.dumps(settings, indent=2) 60 61 62 async def _print_execution_logs(execution) -> None: 63 for msg in execution.logs.stdout: 64 print(f"[stdout] {msg.text}") 65 for msg in execution.logs.stderr: 66 print(f"[stderr] {msg.text}") 67 if execution.error: 68 print(f"[error] {execution.error.name}: {execution.error.value}") 69 70 71 async def main() -> None: 72 domain = os.getenv("SANDBOX_DOMAIN", "localhost:8080") 73 api_key = os.getenv("SANDBOX_API_KEY") 74 qwen_api_key = _required_env("API_KEY") 75 qwen_base_url = os.getenv("BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1") 76 qwen_model_name = os.getenv("MODEL_NAME", "qwen3-coder-plus") 77 image = os.getenv( 78 "SANDBOX_IMAGE", 79 "sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/code-interpreter:v1.0.2", 80 ) 81 82 config = ConnectionConfig( 83 domain=domain, 84 api_key=api_key, 85 request_timeout=timedelta(seconds=60), 86 ) 87 88 sandbox = await Sandbox.create( 89 image, 90 connection_config=config, 91 env={"API_KEY": qwen_api_key}, 92 ) 93 94 async with sandbox: 95 await sandbox.files.create_directories( 96 [ 97 WriteEntry(path=QWEN_PROJECT_DIR, mode=755), 98 WriteEntry(path=QWEN_SETTINGS_DIR, mode=755), 99 ] 100 ) 101 await sandbox.files.write_file( 102 QWEN_SETTINGS_PATH, 103 _build_qwen_settings(qwen_base_url, qwen_model_name), 104 mode=644, 105 ) 106 107 # Install Qwen Code CLI (Node.js is already in the code-interpreter image). 108 install_exec = await sandbox.commands.run( 109 "npm install -g @qwen-code/qwen-code@latest" 110 ) 111 await _print_execution_logs(install_exec) 112 113 # Run Qwen Code in headless mode using the project-local config. 114 run_exec = await sandbox.commands.run( 115 'cd /tmp/qwen-code-example && qwen -p "Compute 1+1 and reply with only the final number."' 116 ) 117 await _print_execution_logs(run_exec) 118 119 await sandbox.kill() 120 121 122 if __name__ == "__main__": 123 asyncio.run(main())