feat: add Tenki sandbox provider (#17305)

### Summary

Adds a `tenki` sandbox provider that runs each agent code execution in a
disposable Tenki (https://tenki.cloud) microVM (create → exec → destroy,
no volumes or snapshots).
Registration mirrors PR #15039, configure `api_key` and `project_id` in
Admin > Sandbox Settings.

Both runtimes are covered:
- Python: `agent/sandbox/providers/tenki.py` (structured results +
artifact collection).
- Go: `internal/agent/sandbox/tenki.go`, mirroring the e2b provider and
wired into the provider manager.

`tenki-sandbox` is an optional dependency (it requires `protobuf>=6.31`,
which differs from RAGFlow's pinned gRPC stack), lazily imported with a
clear error when missing; installation is documented in the sandbox
quickstart.

Unit tests cover execution, structured results, artifacts
(symlink/size/extension limits), non-zero exit, timeout, error mapping,
and idempotent destroy.

---------

Co-authored-by: yiming.wang <yiming.wang@luxor.com>
This commit is contained in:
yiming wang
2026-07-28 19:24:39 +08:00
committed by GitHub
parent 73860170ae
commit dcadd8d837
14 changed files with 1636 additions and 7 deletions

View File

@@ -484,6 +484,11 @@ class SandboxMgr:
"description": "E2B Cloud - Code Execution Sandboxes",
"tags": ["saas", "fast", "global"],
},
"tenki": {
"name": "Tenki",
"description": "Tenki - Disposable microVM code sandboxes",
"tags": ["saas", "cloud", "microvm", "isolated"],
},
}
@staticmethod
@@ -503,6 +508,7 @@ class SandboxMgr:
SSHProvider,
AliyunCodeInterpreterProvider,
E2BProvider,
TenkiProvider,
)
schemas = {
@@ -511,6 +517,7 @@ class SandboxMgr:
"ssh": SSHProvider.get_config_schema(),
"aliyun_codeinterpreter": AliyunCodeInterpreterProvider.get_config_schema(),
"e2b": E2BProvider.get_config_schema(),
"tenki": TenkiProvider.get_config_schema(),
}
if provider_id not in schemas:
@@ -576,6 +583,7 @@ class SandboxMgr:
SSHProvider,
AliyunCodeInterpreterProvider,
E2BProvider,
TenkiProvider,
)
try:
@@ -620,6 +628,7 @@ class SandboxMgr:
"ssh": SSHProvider,
"aliyun_codeinterpreter": AliyunCodeInterpreterProvider,
"e2b": E2BProvider,
"tenki": TenkiProvider,
}
provider = provider_classes[provider_type]()
is_valid, error_msg = provider.validate_config(config)
@@ -667,6 +676,7 @@ class SandboxMgr:
SSHProvider,
AliyunCodeInterpreterProvider,
E2BProvider,
TenkiProvider,
)
# Instantiate provider based on type
@@ -676,6 +686,7 @@ class SandboxMgr:
"ssh": SSHProvider,
"aliyun_codeinterpreter": AliyunCodeInterpreterProvider,
"e2b": E2BProvider,
"tenki": TenkiProvider,
}
if provider_type not in provider_classes: