feat: add Tenki Cloud compute provider - #3242
Conversation
Add Tenki Cloud (https://tenki.cloud) as a compute provider for managed agents, alongside E2B, Daytona, Modal, Fly.io, Docker and local. - TenkiCompute implements ComputeProviderProtocol (provision / execute / shutdown / get_status / upload_file / download_file / list_instances), running tools in disposable Tenki microVMs. Sync SDK wrapped via run_in_executor, matching the existing providers. - Registered as "tenki" in the compute barrel, the _resolve_compute factory, and the compute-provider hint sets. - Uses only stable Tenki features (exec + file I/O). Default stock image installs pip packages on demand; set metadata["tenki_image"] for a custom image. Auto-resolves workspace/project from the API key. - Enabled via TENKI_API_KEY; optional `tenki` extra (tenki-sandbox). - Unit + live (skipped-by-default) tests mirroring the E2B/Daytona suites.
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR adds a Tenki sandbox compute adapter with lifecycle, command execution, file transfer, instance tracking, and package installation support. It exports the adapter, wires ChangesTenki Compute
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant LocalManagedAgent
participant TenkiCompute
participant TenkiSandbox
LocalManagedAgent->>TenkiCompute: resolve compute="tenki"
TenkiCompute->>TenkiSandbox: provision sandbox
TenkiSandbox-->>TenkiCompute: return instance
TenkiCompute->>TenkiSandbox: execute command
TenkiSandbox-->>TenkiCompute: return stdout, stderr, exit code
TenkiCompute->>TenkiSandbox: terminate sandbox
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR adds Tenki Cloud as a managed compute provider. The main changes are:
Confidence Score: 5/5No new blocking issue qualifies for this follow-up review.
|
| Filename | Overview |
|---|---|
| src/praisonai/praisonai/integrations/compute/tenki.py | Implements the Tenki compute provider and includes the latest configuration, cleanup, status, and package-handling fixes. |
| src/praisonai/praisonai/integrations/compute/init.py | Adds the lazy TenkiCompute export. |
| src/praisonai/praisonai/integrations/managed_local.py | Adds Tenki to the managed-local compute resolver. |
| src/praisonai/praisonai/integrations/managed_agents.py | Routes the Tenki provider through the managed compute path. |
| src/praisonai/praisonai/integrations/hosted_agent.py | Adds Tenki to hosted-agent compute provider hints. |
| src/praisonai/pyproject.toml | Adds the optional Tenki SDK dependency. |
| src/praisonai-agents/tests/managed/test_cloud_compute.py | Adds Tenki unit and live integration coverage. |
Reviews (4): Last reviewed commit: "fix(tenki): derive image default from Co..." | Re-trigger Greptile
|
@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding. Phase 1: Review per AGENTS.md
Phase 2: FIX Valid Issues Phase 3: Final Verdict |
|
Claude finished @MervinPraison's task —— View job Final Architecture Review — Tenki Compute Provider ✅Phase 1 — Architecture (AGENTS.md): PASS
Phase 2 — Fixes pushed to
Skipped (with reason):
Phase 3 — Verdict: ✅ Approve. All valid, in-scope issues resolved; changes are minimal and backward-compatible. Tests:
|
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/praisonai/praisonai/integrations/compute/tenki.py`:
- Around line 33-40: Update the TenkiCompute docstring example to configure the
image through the metadata key consumed by _provision_sync, replacing the
ineffective ComputeConfig image argument while preserving the existing
provisioning and execution flow.
- Around line 67-84: Update _resolve_ids to validate explicitly configured
workspace and project IDs instead of silently falling back. If _workspace_id is
set but absent from identity.workspaces, raise an error; if _project_id is set
but absent from the selected workspace’s projects, raise an error. Preserve
first-entry fallback only when the corresponding ID is unset.
- Around line 158-165: Update _shutdown_sync so self._sandboxes retains the
instance until sandbox.terminate() succeeds; only remove instance_id after
confirmed termination. If termination raises, keep the sandbox tracked and
preserve the warning log so status/listing and a later retry can reconcile the
running resource.
- Around line 60-62: Update the ImportError handling around the Tenki SDK import
to capture the original exception and chain it when raising the installation
guidance error. Preserve the existing message while using the caught exception
as the explicit cause.
- Around line 281-312: Secure package installation in _install_packages_sync by
applying the same pip specifier validation and per-token shlex.quote handling
used by managed_local.py’s _install_packages_in_compute; apply equivalent safe
quoting and validation to npm_pkgs before constructing the bash commands,
rejecting malformed entries rather than interpolating them. Preserve the
existing installation and warning behavior for valid packages.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8999eb68-865f-4510-84db-d26b2687097c
📒 Files selected for processing (7)
src/praisonai-agents/tests/managed/test_cloud_compute.pysrc/praisonai/praisonai/integrations/compute/__init__.pysrc/praisonai/praisonai/integrations/compute/tenki.pysrc/praisonai/praisonai/integrations/hosted_agent.pysrc/praisonai/praisonai/integrations/managed_agents.pysrc/praisonai/praisonai/integrations/managed_local.pysrc/praisonai/pyproject.toml
…eanup) The maintainer's review bot flagged these but couldn't push to a fork, so applying directly: - Raise on a configured-but-unknown TENKI_WORKSPACE_ID/PROJECT_ID instead of silently using the first workspace/project (wrong-workspace billing). - shlex.quote every pip/npm spec (command-injection hardening). - Honour a non-default ComputeConfig.image, not just metadata["tenki_image"]. - Respect ComputeConfig.networking (restricted -> allow_outbound=False). - Fail provisioning and tear down the sandbox when package install fails (no false RUNNING / leaked sandbox). - Terminate before dropping the handle in shutdown, so a failed terminate keeps the sandbox tracked for retry instead of silently leaking it.
|
Thanks for the thorough automated review! One heads-up: the review notes say the fixes were pushed to
Agree on the one skipped item (reconciling live remote state in Re-validated live against real Tenki (SDK 0.4.0): happy path + clean shutdown, plus both new raise-paths (bad workspace id → no sandbox created; failed install → sandbox torn down, no leak). Also on |
|
@claude validate if this need to go to MervinPraison/PraisonAI-Plugins |
|
Claude finished @MervinPraison's task —— View job Routing Validation — Does Tenki belong in PraisonAI-Plugins?Verdict: No. It correctly stays in
WhyThe repo has two distinct sandbox/compute subsystems, and the PraisonAI-Plugins routing rule only applies to one of them:
Tenki implements Moving Tenki to PraisonAI-Plugins would:
Architecture check (already satisfied)
The View job · branch |
- get_status/list_instances now refresh remote Tenki state (sandbox.refresh + .state) instead of trusting the local map, so a server-side idle timeout no longer surfaces as RUNNING while execute() hits a dead sandbox. Mirrors the E2B provider's is_running() reconciliation. - add exception chaining on the SDK ImportError (raise ... from e) - drop the misleading docstring image example (it passed the default sentinel that is intentionally treated as the stock image)
|
Follow-up — pushed 1. Remote shutdown / stale RUNNING state (Greptile) — I originally deferred this as "matches E2B/Daytona," but that was only half right: E2B does reconcile remote state via 2. Exception chaining (CodeRabbit) — the SDK 3. Docstring example (CodeRabbit) — dropped the misleading Tenki unit tests pass locally. Compatibility note: |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/praisonai/praisonai/integrations/compute/tenki.py (1)
66-98: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winCache the resolved Tenki IDs with an explicit flag.
if self._workspace_id and self._project_id:returns before callingclient.who_am_i(), so constructor/env-configured IDs skip the lookup/presence checks and are passed straight toclient.create(). Track the first successful resolution withself._ids_resolved = Trueand use that as the cache guard.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/praisonai/praisonai/integrations/compute/tenki.py` around lines 66 - 98, Update _resolve_ids to use an explicit self._ids_resolved cache guard instead of checking self._workspace_id and self._project_id; perform the workspace/project lookup and validation on the first call, then set self._ids_resolved = True only after successful resolution before returning the IDs.
🧹 Nitpick comments (2)
src/praisonai/praisonai/integrations/compute/tenki.py (2)
316-339: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueSequential per-instance network round-trips in
list_instances.Each tracked sandbox triggers a blocking
sandbox.refresh()call via_is_runninginside a plain loop, solist_instances()latency scales linearly with the number of tracked sandboxes. Since this already runs inside an executor thread, consider fanning the refresh calls out concurrently (e.g. a small thread pool orasyncio.gatherover per-sandbox executor calls) if the instance count can grow beyond a handful.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/praisonai/praisonai/integrations/compute/tenki.py` around lines 316 - 339, Update _list_instances_sync so sandbox liveness checks for all tracked instances run concurrently using a bounded thread pool, while preserving the existing filtering and InstanceInfo construction behavior. Avoid unbounded worker creation and keep list_instances’ executor-based async interface unchanged.
134-138: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMagic-string default comparison is fragile.
Detecting a "custom" image by comparing
config.imageagainst the hardcoded literal"python:3.12-slim"silently breaks in two ways: ifComputeConfig's default ever changes inpraisonaiagents.managed.protocols, this comparison goes stale unnoticed; and a caller who explicitly requests"python:3.12-slim"(matching today's default) is treated as "unset" and gets Tenki's stock image instead. Consider deriving the sentinel from the dataclass field default (e.g.ComputeConfig.__dataclass_fields__["image"].default) instead of duplicating the literal here, or documenting the coupling explicitly.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/praisonai/praisonai/integrations/compute/tenki.py` around lines 134 - 138, Replace the hardcoded "python:3.12-slim" comparison in the Tenki image-selection logic with the authoritative default from ComputeConfig's image dataclass field, while preserving metadata precedence and Tenki's stock-image fallback. Ensure an explicitly supplied image equal to the current default remains distinguishable from an unset value where the configuration model supports that distinction.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/praisonai/praisonai/integrations/compute/tenki.py`:
- Around line 66-98: Update _resolve_ids to use an explicit self._ids_resolved
cache guard instead of checking self._workspace_id and self._project_id; perform
the workspace/project lookup and validation on the first call, then set
self._ids_resolved = True only after successful resolution before returning the
IDs.
---
Nitpick comments:
In `@src/praisonai/praisonai/integrations/compute/tenki.py`:
- Around line 316-339: Update _list_instances_sync so sandbox liveness checks
for all tracked instances run concurrently using a bounded thread pool, while
preserving the existing filtering and InstanceInfo construction behavior. Avoid
unbounded worker creation and keep list_instances’ executor-based async
interface unchanged.
- Around line 134-138: Replace the hardcoded "python:3.12-slim" comparison in
the Tenki image-selection logic with the authoritative default from
ComputeConfig's image dataclass field, while preserving metadata precedence and
Tenki's stock-image fallback. Ensure an explicitly supplied image equal to the
current default remains distinguishable from an unset value where the
configuration model supports that distinction.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 51c0e496-6fa4-491b-a16b-639082168dbb
📒 Files selected for processing (1)
src/praisonai/praisonai/integrations/compute/tenki.py
Reads the 'unchanged default = use stock image' sentinel off ComputeConfig's dataclass field default instead of hardcoding "python:3.12-slim", so the image-selection logic can't silently drift if that default changes (CodeRabbit out-of-diff nit).
|
Thanks @coderabbitai — the out-of-diff findings, addressed in Fixed — magic-string default ( Skipped, with reasoning:
|
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
tenki_file=$(fd -a '^tenki\.py$' | head -n1)
e2b_file=$(fd -a '^e2b\.py$' | head -n1)
config_file=$(rg -l --type py -n 'class\s+ComputeConfig\b' | head -n1)
printf '%s\n' '=== Commit 44631dc6 ==='
git show --stat --oneline 44631dc6
git show --format= -- "$tenki_file"
printf '%s\n' '=== Tenki structure and relevant sections ==='
ast-grep outline "$tenki_file" --items all --view expanded
nl -ba "$tenki_file" | sed -n '45,155p;300,355p'
printf '%s\n' '=== ComputeConfig declaration ==='
ast-grep outline "$config_file" --items all --view expanded
rg -n -A35 -B5 'class\s+ComputeConfig\b' "$config_file"
printf '%s\n' '=== E2B list_instances comparison ==='
ast-grep outline "$e2b_file" --items all --view expanded
rg -n -A45 -B5 'async\s+def\s+list_instances\b' "$e2b_file"Length of output: 1117 🏁 Script executed: #!/bin/bash
set -u
printf '%s\n' '=== Commit availability and changed paths ==='
git show --stat --oneline 44631dc6 2>&1 || true
printf '%s\n' '=== Candidate compute implementation files ==='
fd -a -t f -e py . | rg '/(tenki|e2b)\.py$|compute.*\.py$' || true
printf '%s\n' '=== Tenki adapter: default selection, ID resolution, list_instances ==='
rg -n -A20 -B15 --type py \
'dataclasses\.fields|ComputeConfig|workspace_id|project_id|who_am_i|async\s+def\s+list_instances' \
. -g '*tenki*.py' || true
printf '%s\n' '=== ComputeConfig declaration ==='
rg -n -A45 -B5 --type py 'class\s+ComputeConfig\b' . || true
printf '%s\n' '=== E2B list_instances implementation ==='
rg -n -A55 -B8 --type py 'async\s+def\s+list_instances\b' . -g '*e2b*.py' || trueLength of output: 30548
Your reasoning on the two deferred items also checks out:
🐇 ✅ ✏️ Learnings added
If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! |
What & why
PraisonAI already ships compute providers for E2B, Daytona, Modal, Fly.io, Docker and local (one file per vendor in
integrations/compute/). This adds Tenki Cloud as another option — disposable Linux microVMs — for running managed-agent tools.What it does
TenkiComputeimplements the fullComputeProviderProtocol(provision/execute/shutdown/get_status/upload_file/download_file/list_instances), running tools in ephemeral Tenki microVMs. Sync SDK wrapped inrun_in_executor, exactly likeDaytonaCompute/E2BCompute."tenki"in the compute barrel (__init__.py), the_resolve_computefactory (managed_local.py), and the provider hint sets (managed_agents.py,hosted_agent.py).TENKI_API_KEY; optionaltenkiextra (tenki-sandbox>=0.4.0). Auto-resolves workspace/project from the key.Feature scope
Stable Tenki primitives only — ephemeral
exec+ file I/O (no volume/snapshot/template). The stock image shipspython3;config.packagesare installed on demand. Setconfig.metadata["tenki_image"]to boot a prebaked image instead.Testing
provider_name,is_available, nonexistent-instance handling, barrel export — mirroring the E2B/Daytona suites.TENKI_API_KEYis set): provision → execute → file upload/download → shutdown, plus pip-install.Summary by CodeRabbit