Class: ActionAgent::Api::SandboxesController

Inherits:
BaseController show all
Defined in:
app/controllers/action_agent/api/sandboxes_controller.rb

Instance Method Summary collapse

Methods inherited from ActionAgent::ApplicationController

allow_unauthenticated_access

Instance Method Details

#compareObject

POST /api/sandboxes/compare Run multiple providers in a single sandbox using parallel generation jobs



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'app/controllers/action_agent/api/sandboxes_controller.rb', line 20

def compare
  providers = params[:providers] || %w[anthropic openai ollama]
  task = params[:task]
  sandbox_id = params[:sandbox_id]

  return render json: { error: "Task required" }, status: :bad_request unless task.present?
  return render json: { error: "At least 2 providers required" }, status: :bad_request if providers.size < 2

  # Validate providers
  invalid = providers - %w[anthropic openai ollama]
  return render json: { error: "Invalid providers: #{invalid.join(', ')}" }, status: :bad_request if invalid.any?

  # Use existing sandbox or create a new one (single container per user)
  sandbox = if sandbox_id.present?
    owned(SandboxSession).find_by!(session_id: sandbox_id)
  else
    s = SandboxSession.new(sandbox_type: params[:sandbox_type] || "playwright_mcp")
    s.user = current_user if s.respond_to?(:user=)
    s.save!
    s.provision!
    s.reload
    s
  end

  unless sandbox.can_run?
    return render json: {
      error: sandbox.expired? ? "Session expired" : "Maximum runs exceeded",
      sandbox: sandbox.summary
    }, status: :unprocessable_entity
  end

  sandbox.update!(status: :running)
  comparison_id = SecureRandom.uuid

  # Spawn a separate generation job for each provider (all in same sandbox)
  runs = providers.map do |provider|
    run_id = SecureRandom.uuid
    SandboxRunJob.perform_later(sandbox.id, run_id, task, provider)

    {
      provider: provider,
      run_id: run_id,
      status: "running"
    }
  end

  render json: {
    comparison_id: comparison_id,
    task: task,
    sandbox: sandbox.summary,
    runs: runs
  }, status: :accepted
end

#createObject

POST /api/sandboxes Create a new sandbox session, owned by whoever opened it.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'app/controllers/action_agent/api/sandboxes_controller.rb', line 87

def create
  @sandbox = SandboxSession.new(sandbox_params)
  # Guarded: the association only exists when the host app configured a
  # user model, and a single-user install configures none.
  @sandbox.user = current_user if @sandbox.respond_to?(:user=)
  @sandbox.agent_template = AgentTemplate.find_by(slug: params[:template_slug]) if params[:template_slug]

  if @sandbox.save
    @sandbox.provision!
    @sandbox.reload # Reload to get updated status after provisioning
    render json: { sandbox: @sandbox.summary }, status: :created
  else
    render json: { errors: @sandbox.errors.full_messages }, status: :unprocessable_entity
  end
end

#destroyObject

DELETE /api/sandboxes/:session_id End sandbox session



155
156
157
158
# File 'app/controllers/action_agent/api/sandboxes_controller.rb', line 155

def destroy
  @sandbox.expire!
  render json: { deleted: true }
end

#indexObject

GET /api/sandboxes List available sandbox types and sample tasks



76
77
78
79
80
81
82
83
# File 'app/controllers/action_agent/api/sandboxes_controller.rb', line 76

def index
  render json: {
    sandbox_types: SandboxSession::SANDBOX_TYPES,
    free_tier_limits: SandboxSession::FREE_TIER_LIMITS,
    templates: free_tier_templates,
    sample_tasks: sample_tasks
  }
end

#runObject

POST /api/sandboxes/:session_id/run Execute a task in the sandbox



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'app/controllers/action_agent/api/sandboxes_controller.rb', line 111

def run
  unless @sandbox.can_run?
    return render json: {
      error: @sandbox.expired? ? "Session expired" : "Maximum runs exceeded",
      sandbox: @sandbox.summary
    }, status: :unprocessable_entity
  end

  # Whatever limits the host app imposes on running an agent.
  if (denial = ActionAgent.quota_denial(current_owner, :execution))
    return render json: {
      error: "Plan limit reached",
      upgrade_required: true,
      message: denial
    }, status: :payment_required
  end

  task = params[:task]
  return render json: { error: "Task required" }, status: :bad_request unless task.present?

  # Provider selection (default to anthropic)
  provider = params[:provider] || "anthropic"
  unless %w[anthropic openai ollama].include?(provider)
    return render json: { error: "Invalid provider" }, status: :bad_request
  end

  record_execution_usage

  @sandbox.update!(status: :running)

  # Execute via job for async processing
  run_id = SecureRandom.uuid
  SandboxRunJob.perform_later(@sandbox.id, run_id, task, provider)

  render json: {
    run_id: run_id,
    status: "running",
    provider: provider,
    sandbox: @sandbox.summary
  }, status: :accepted
end

#showObject

GET /api/sandboxes/:session_id Get sandbox status and run history



105
106
107
# File 'app/controllers/action_agent/api/sandboxes_controller.rb', line 105

def show
  render json: { sandbox: @sandbox.details }
end