Skip to content

Commit c3b8d8b

Browse files
yongtengleiKevinHuShwritinwaters
authored
Refa: improve usability of Node.js/JavaScript code executor (#8979)
### What problem does this PR solve? Improve usability of Node.js/JavaScript code executor. ### Type of change - [x] Refactoring --------- Co-authored-by: Kevin Hu <kevinhu.sh@gmail.com> Co-authored-by: writinwaters <93570324+writinwaters@users.noreply.github.com>
1 parent f63ad6b commit c3b8d8b

File tree

5 files changed

+75
-13
lines changed

5 files changed

+75
-13
lines changed

sandbox/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,42 @@ To add Node.js dependencies:
213213

214214
---
215215

216+
217+
## Usage
218+
219+
### 🐍 A Python example
220+
221+
```python
222+
def main(arg1: str, arg2: str) -> str:
223+
return f"result: {arg1 + arg2}"
224+
```
225+
226+
### 🟨 JavaScript examples
227+
228+
A simple sync function
229+
230+
```javascript
231+
function main({arg1, arg2}) {
232+
return arg1+arg2
233+
}
234+
```
235+
236+
Async funcion with aioxs
237+
238+
```javascript
239+
const axios = require('axios');
240+
async function main() {
241+
try {
242+
const response = await axios.get('https://github.com/infiniflow/ragflow');
243+
return 'Body:' + response.data;
244+
} catch (error) {
245+
return 'Error:' + error.message;
246+
}
247+
}
248+
```
249+
250+
---
251+
216252
## 📋 FAQ
217253

218254
### ❓Sandbox Not Working?

sandbox/executor_manager/api/handlers.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,29 @@
1515
#
1616
import base64
1717

18+
from core.container import _CONTAINER_EXECUTION_SEMAPHORES
1819
from core.logger import logger
1920
from fastapi import Request
20-
from models.enums import ResultStatus
21+
from models.enums import ResultStatus, SupportLanguage
2122
from models.schemas import CodeExecutionRequest, CodeExecutionResult
2223
from services.execution import execute_code
2324
from services.limiter import limiter
2425
from services.security import analyze_code_security
25-
from core.container import _CONTAINER_EXECUTION_SEMAPHORES
26+
2627

2728
async def healthz_handler():
2829
return {"status": "ok"}
2930

31+
3032
@limiter.limit("5/second")
3133
async def run_code_handler(req: CodeExecutionRequest, request: Request):
3234
logger.info("🟢 Received /run request")
3335

3436
async with _CONTAINER_EXECUTION_SEMAPHORES[req.language]:
3537
code = base64.b64decode(req.code_b64).decode("utf-8")
38+
if req.language == SupportLanguage.NODEJS:
39+
code += "\n\nmodule.exports = { main };"
40+
req.code_b64 = base64.b64encode(code.encode("utf-8")).decode("utf-8")
3641
is_safe, issues = analyze_code_security(code, language=req.language)
3742
if not is_safe:
3843
issue_details = "\n".join([f"Line {lineno}: {issue}" for issue, lineno in issues])

sandbox/executor_manager/api/routes.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@
2020
router = APIRouter()
2121

2222
router.get("/healthz")(healthz_handler)
23-
router.post("/run")(run_code_handler)
23+
router.post("/run")(run_code_handler)
24+

sandbox/executor_manager/core/container.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
_CONTAINER_QUEUES: dict[SupportLanguage, Queue] = {}
2828
_CONTAINER_LOCK: asyncio.Lock = asyncio.Lock()
29-
_CONTAINER_EXECUTION_SEMAPHORES:dict[SupportLanguage,asyncio.Semaphore] = {}
29+
_CONTAINER_EXECUTION_SEMAPHORES: dict[SupportLanguage, asyncio.Semaphore] = {}
3030

3131

3232
async def init_containers(size: int) -> tuple[int, int]:
@@ -38,7 +38,7 @@ async def init_containers(size: int) -> tuple[int, int]:
3838
_CONTAINER_QUEUES[SupportLanguage.PYTHON].get_nowait()
3939
while not _CONTAINER_QUEUES[SupportLanguage.NODEJS].empty():
4040
_CONTAINER_QUEUES[SupportLanguage.NODEJS].get_nowait()
41-
41+
4242
for language in SupportLanguage:
4343
_CONTAINER_EXECUTION_SEMAPHORES[language] = asyncio.Semaphore(size)
4444

sandbox/executor_manager/services/execution.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,40 @@ async def execute_code(req: CodeExecutionRequest):
8282
const path = require('path');
8383
8484
const args = JSON.parse(process.argv[2]);
85-
8685
const mainPath = path.join(__dirname, 'main.js');
8786
87+
function isPromise(value) {
88+
return Boolean(value && typeof value.then === 'function');
89+
}
90+
8891
if (fs.existsSync(mainPath)) {
89-
const { main } = require(mainPath);
92+
const mod = require(mainPath);
93+
const main = typeof mod === 'function' ? mod : mod.main;
94+
95+
if (typeof main !== 'function') {
96+
console.error('Error: main is not a function');
97+
process.exit(1);
98+
}
9099
91100
if (typeof args === 'object' && args !== null) {
92-
main(args).then(result => {
93-
if (result !== null) {
94-
console.log(result);
101+
try {
102+
const result = main(args);
103+
if (isPromise(result)) {
104+
result.then(output => {
105+
if (output !== null) {
106+
console.log(output);
107+
}
108+
}).catch(err => {
109+
console.error('Error in async main function:', err);
110+
});
111+
} else {
112+
if (result !== null) {
113+
console.log(result);
114+
}
95115
}
96-
}).catch(err => {
97-
console.error('Error in main function:', err);
98-
});
116+
} catch (err) {
117+
console.error('Error when executing main:', err);
118+
}
99119
} else {
100120
console.error('Error: args is not a valid object:', args);
101121
}

0 commit comments

Comments
 (0)