Browse Source

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>
tags/v0.20.0
Yongteng Lei 3 months ago
parent
commit
c3b8d8b4ba
No account linked to committer's email address

+ 36
- 0
sandbox/README.md View File

@@ -213,6 +213,42 @@ To add Node.js dependencies:

---


## Usage

### 🐍 A Python example

```python
def main(arg1: str, arg2: str) -> str:
return f"result: {arg1 + arg2}"
```

### 🟨 JavaScript examples

A simple sync function

```javascript
function main({arg1, arg2}) {
return arg1+arg2
}
```

Async funcion with aioxs

```javascript
const axios = require('axios');
async function main() {
try {
const response = await axios.get('https://github.com/infiniflow/ragflow');
return 'Body:' + response.data;
} catch (error) {
return 'Error:' + error.message;
}
}
```

---

## 📋 FAQ

### ❓Sandbox Not Working?

+ 7
- 2
sandbox/executor_manager/api/handlers.py View File

@@ -15,24 +15,29 @@
#
import base64

from core.container import _CONTAINER_EXECUTION_SEMAPHORES
from core.logger import logger
from fastapi import Request
from models.enums import ResultStatus
from models.enums import ResultStatus, SupportLanguage
from models.schemas import CodeExecutionRequest, CodeExecutionResult
from services.execution import execute_code
from services.limiter import limiter
from services.security import analyze_code_security
from core.container import _CONTAINER_EXECUTION_SEMAPHORES

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


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

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

+ 2
- 1
sandbox/executor_manager/api/routes.py View File

@@ -20,4 +20,5 @@ from api.handlers import healthz_handler, run_code_handler
router = APIRouter()

router.get("/healthz")(healthz_handler)
router.post("/run")(run_code_handler)
router.post("/run")(run_code_handler)


+ 2
- 2
sandbox/executor_manager/core/container.py View File

@@ -26,7 +26,7 @@ from core.logger import logger

_CONTAINER_QUEUES: dict[SupportLanguage, Queue] = {}
_CONTAINER_LOCK: asyncio.Lock = asyncio.Lock()
_CONTAINER_EXECUTION_SEMAPHORES:dict[SupportLanguage,asyncio.Semaphore] = {}
_CONTAINER_EXECUTION_SEMAPHORES: dict[SupportLanguage, asyncio.Semaphore] = {}


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


+ 28
- 8
sandbox/executor_manager/services/execution.py View File

@@ -82,20 +82,40 @@ const fs = require('fs');
const path = require('path');

const args = JSON.parse(process.argv[2]);

const mainPath = path.join(__dirname, 'main.js');

function isPromise(value) {
return Boolean(value && typeof value.then === 'function');
}

if (fs.existsSync(mainPath)) {
const { main } = require(mainPath);
const mod = require(mainPath);
const main = typeof mod === 'function' ? mod : mod.main;

if (typeof main !== 'function') {
console.error('Error: main is not a function');
process.exit(1);
}

if (typeof args === 'object' && args !== null) {
main(args).then(result => {
if (result !== null) {
console.log(result);
try {
const result = main(args);
if (isPromise(result)) {
result.then(output => {
if (output !== null) {
console.log(output);
}
}).catch(err => {
console.error('Error in async main function:', err);
});
} else {
if (result !== null) {
console.log(result);
}
}
}).catch(err => {
console.error('Error in main function:', err);
});
} catch (err) {
console.error('Error when executing main:', err);
}
} else {
console.error('Error: args is not a valid object:', args);
}

Loading…
Cancel
Save