浏览代码

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 个月前
父节点
当前提交
c3b8d8b4ba
没有帐户链接到提交者的电子邮件

+ 36
- 0
sandbox/README.md 查看文件



--- ---



## 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 ## 📋 FAQ


### ❓Sandbox Not Working? ### ❓Sandbox Not Working?

+ 7
- 2
sandbox/executor_manager/api/handlers.py 查看文件

# #
import base64 import base64


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


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



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


async with _CONTAINER_EXECUTION_SEMAPHORES[req.language]: async with _CONTAINER_EXECUTION_SEMAPHORES[req.language]:
code = base64.b64decode(req.code_b64).decode("utf-8") 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) is_safe, issues = analyze_code_security(code, language=req.language)
if not is_safe: if not is_safe:
issue_details = "\n".join([f"Line {lineno}: {issue}" for issue, lineno in issues]) issue_details = "\n".join([f"Line {lineno}: {issue}" for issue, lineno in issues])

+ 2
- 1
sandbox/executor_manager/api/routes.py 查看文件

router = APIRouter() router = APIRouter()


router.get("/healthz")(healthz_handler) 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 查看文件



_CONTAINER_QUEUES: dict[SupportLanguage, Queue] = {} _CONTAINER_QUEUES: dict[SupportLanguage, Queue] = {}
_CONTAINER_LOCK: asyncio.Lock = asyncio.Lock() _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]: async def init_containers(size: int) -> tuple[int, int]:
_CONTAINER_QUEUES[SupportLanguage.PYTHON].get_nowait() _CONTAINER_QUEUES[SupportLanguage.PYTHON].get_nowait()
while not _CONTAINER_QUEUES[SupportLanguage.NODEJS].empty(): while not _CONTAINER_QUEUES[SupportLanguage.NODEJS].empty():
_CONTAINER_QUEUES[SupportLanguage.NODEJS].get_nowait() _CONTAINER_QUEUES[SupportLanguage.NODEJS].get_nowait()
for language in SupportLanguage: for language in SupportLanguage:
_CONTAINER_EXECUTION_SEMAPHORES[language] = asyncio.Semaphore(size) _CONTAINER_EXECUTION_SEMAPHORES[language] = asyncio.Semaphore(size)



+ 28
- 8
sandbox/executor_manager/services/execution.py 查看文件

const path = require('path'); const path = require('path');


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

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


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

if (fs.existsSync(mainPath)) { 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) { 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 { } else {
console.error('Error: args is not a valid object:', args); console.error('Error: args is not a valid object:', args);
} }

正在加载...
取消
保存