メインコンテンツへスキップ

概要

Jinba Toolboxは、隔離されたSandbox環境でToolコードを実行します。Sandboxシステムはプロバイダー非依存のアーキテクチャで設計されています。共有インターフェースパッケージ(@jinba-toolbox/sandbox-core)がコントラクトを定義し、個別のプロバイダーパッケージ(sandbox-e2bsandbox-daytona)がそれを実装します。 この分離により、ビジネスロジックを変更することなく、プロバイダーの切り替えや新規追加が可能になります。

アーキテクチャ

┌─────────────────────────────────────────────────────────────┐
│                     API Server                              │
│                                                             │
│  Execution Service ──▶ SandboxProvider interface            │
│                                                             │
├─────────────────────────────────────────────────────────────┤
│                  Provider Implementations                   │
│       ┌────────────────┐       ┌─────────────────┐         │
│       │ sandbox-e2b    │       │ sandbox-daytona  │         │
│       │ (E2B Code      │       │ (Daytona SDK)    │         │
│       │  Interpreter)  │       │                  │         │
│       └────────────────┘       └─────────────────┘         │
├─────────────────────────────────────────────────────────────┤
│                    sandbox-core                              │
│           (Interfaces & Types only)                         │
└─────────────────────────────────────────────────────────────┘

コアインターフェース

@jinba-toolbox/sandbox-core パッケージにはインターフェースと型のみが含まれ、具体的な実装はありません。すべてのプロバイダーはこれらのコントラクトに準拠する必要があります。

SandboxProvider

Sandboxインスタンスを作成・再開するファクトリ:
interface SandboxProvider {
  /** 新しいSandboxインスタンスを作成 */
  create(options: SandboxCreateOptions): Promise<SandboxInstance>;
  /** IDで既存のSandboxを再開 */
  resume(sandboxId: string): Promise<SandboxInstance>;
}

SandboxInstance

コード実行、シェルコマンド、ファイルシステムアクセスを備えた実行中のSandbox環境:
interface SandboxInstance {
  readonly sandboxId: string;
  readonly commands: SandboxCommands;
  readonly filesystem: SandboxFilesystem;

  executeCode(
    code: string,
    language: SandboxLanguage,
    options?: CodeExecutionOptions
  ): Promise<CodeExecutionResult>;

  kill(): Promise<void>;
  pause(): Promise<void>;
  getHost(port: number): Promise<string>;
}

SandboxCommands

Sandbox内でのシェルコマンド実行:
interface SandboxCommands {
  run(
    command: string,
    options?: SandboxCommandOptions
  ): Promise<SandboxExecutionResult>;
}

SandboxFilesystem

Sandbox内でのファイル操作:
interface SandboxFilesystem {
  read(path: string): Promise<string>;
  readBytes(path: string): Promise<Uint8Array>;
  write(path: string, content: string | Uint8Array): Promise<void>;
  list(path: string): Promise<SandboxFileInfo[]>;
  exists(path: string): Promise<boolean>;
  mkdir(path: string): Promise<void>;
  remove(path: string): Promise<void>;
}

主要な型

type SandboxLanguage = "python" | "typescript";

interface SandboxCreateOptions {
  language: SandboxLanguage;
  packages?: string[];               // インストールするパッケージ
  envs?: Record<string, string>;     // 環境変数
  workingDirectory?: string;         // Sandbox内の作業ディレクトリ
  timeoutMs?: number;                // タイムアウト(ミリ秒)
}

interface CodeExecutionResult {
  success: boolean;
  output?: unknown;       // 利用可能な場合はパース済みJSON出力
  stdout: string;
  stderr: string;
  durationMs: number;
  error?: {
    name: string;
    message: string;
    traceback?: string;
  };
}

interface SandboxExecutionResult {
  exitCode: number;
  stdout: string;
  stderr: string;
}

E2Bプロバイダー

@jinba-toolbox/sandbox-e2b パッケージは、E2B Code Interpreter SDKを使用してSandboxインターフェースを実装しています。

設定

import { createE2BProvider } from "@jinba-toolbox/sandbox-e2b";

const provider = createE2BProvider({
  apiKey: process.env.E2B_API_KEY!,
  templateId: "custom-template",  // オプションのカスタムSandboxテンプレート
  timeoutMs: 300000,              // デフォルト: 5分
});
オプションデフォルト説明
apiKeystring(必須)E2B APIキー
templateIdstringundefinedカスタムSandboxテンプレートID
timeoutMsnumber300000デフォルトタイムアウト(5分)

動作の仕組み

  1. Sandboxの作成: プロバイダーがE2B Code Interpreter Sandboxを作成します。オプションでカスタムテンプレートを使用できます。
  2. パッケージのインストール: パッケージが指定されている場合、Sandbox内で pip install(Python)または npm install(TypeScript)が実行されます。
  3. コードの実行: ToolコードはE2Bの runCode APIを介してJupyterライクなカーネルで実行されます。
  4. ライフサイクル: Sandboxは一時停止、再開、または終了できます。

TypeScriptに関する注意

E2BのCode Interpreterは、ESMファイルインポートをサポートしないJupyterカーネルでTypeScriptを実行します。このため、TypeScript Toolのコードはファイルからの読み込みではなくインライン化されます。Python Toolは通常通りファイルシステムインポートを使用できます。

Daytonaプロバイダー

@jinba-toolbox/sandbox-daytona パッケージは、Daytona SDKを使用してSandboxインターフェースを実装しています。

設定

import { createDaytonaProvider } from "@jinba-toolbox/sandbox-daytona";

const provider = createDaytonaProvider({
  apiKey: process.env.DAYTONA_API_KEY!,
  apiUrl: "https://app.daytona.io/api",  // デフォルト
  target: "us",                           // リージョン: "us" または "eu"
  timeoutMs: 300000,                      // デフォルト: 5分
});
オプションデフォルト説明
apiKeystring(必須)Daytona APIキー
apiUrlstringhttps://app.daytona.io/apiDaytona API URL
targetstringusターゲットリージョン(us または eu
timeoutMsnumber300000デフォルトタイムアウト(5分)

動作の仕組み

  1. Sandboxの作成: プロバイダーがDaytona SDKを初期化し、指定された言語でSandboxを作成します。
  2. パッケージのインストール: パッケージが指定されている場合、プロバイダーがSandbox内で pip install または npm install を実行します。
  3. コードの実行: ToolコードはDaytonaの process.codeRun APIを介して実行されます。
  4. ライフサイクル: Sandboxは起動、停止、削除が可能です。

ToolSetのSandbox設定

ToolSetを作成する際に、使用するSandboxプロバイダーと設定を指定します:
await client.createToolSet({
  slug: "data-tools",
  name: { default: "Data Tools" },
  description: { default: "Data processing tools" },
  sandbox: {
    provider: "e2b",          // or "daytona"
    language: "python",       // or "typescript"
    packages: [
      { name: "pandas", version: "2.0" },
      { name: "numpy" }
    ],
    resources: {
      cpu: "2",
      memory: "4Gi",
      timeout: 120000
    }
  }
});
各ToolSetに保存される SandboxConfig
interface SandboxConfig {
  provider: "e2b" | "daytona";
  language: "python" | "typescript";
  runtime?: string;
  packages: { name: string; version?: string }[];
  resources: {
    cpu?: string;
    memory?: string;
    timeout?: number;
  };
}

エラーハンドリング

すべてのSandboxパッケージは統一された SandboxError 型を使用します:
class SandboxError extends Error {
  constructor(
    message: string,
    code: SandboxErrorCode,
    cause?: unknown
  );
}

type SandboxErrorCode =
  | "CONNECTION_FAILED"    // プロバイダーへの接続失敗
  | "TIMEOUT"              // 実行がタイムアウト
  | "NOT_FOUND"            // Sandboxが見つからない(再開時)
  | "EXECUTION_FAILED"     // コード実行の失敗
  | "INTERNAL";            // 予期しない内部エラー

新しいプロバイダーの追加

アーキテクチャは拡張性を考慮して設計されています。新しいSandboxプロバイダーを追加するには:
1

新しいパッケージを作成する

標準的なパッケージ構成で packages/sandbox-<provider> を作成します。
2

SandboxProviderインターフェースを実装する

SandboxInstance を返す create()resume() メソッドを実装します。
3

プロバイダーを登録する

APIサーバーの getSandboxProvider 関数にプロバイダーのケースを追加します。sandbox-core やビジネスロジックの変更は不要です。

関連ドキュメント