Module: RobotLab::Web

Defined in:
lib/robot_lab/web.rb,
lib/robot_lab/web/app.rb,
lib/robot_lab/web/event.rb,
lib/robot_lab/web/version.rb,
lib/robot_lab/web/registry.rb,
lib/robot_lab/web/event_sink.rb,
lib/robot_lab/web/stream_hook.rb,
lib/robot_lab/web/activity_log.rb,
lib/robot_lab/web/components/chat.rb,
lib/robot_lab/web/components/layout.rb,
lib/robot_lab/web/components/message.rb,
lib/robot_lab/web/components/dashboard.rb,
lib/robot_lab/web/components/error_page.rb,
sig/robot_lab/web.rbs

Overview

Browser front end for a robot_lab project: stream a robot's run to a web page over Server-Sent Events (an on_event -> SSE bridge, an immutable Event value object, an in-memory ActivityLog, and a hardened dev-tool security posture).

Defined Under Namespace

Modules: Components, EventSink, Registry Classes: ActivityLog, App, Error, Event, StreamHook

Constant Summary collapse

VERSION =

Returns:

  • (String)
"0.2.6"

Class Method Summary collapse

Class Method Details

.appObject

Boot the Sinatra app. Requires the opt-in web stack on first call.

RobotLab::Web.app  # => RobotLab::Web::App (a Rack app)


72
73
74
75
# File 'lib/robot_lab/web.rb', line 72

def app
  require_relative 'web/app'
  App
end

.register(robot, name: nil) ⇒ Object

Register a robot the web UI can drive. Returns the string key.

RobotLab::Web.register(my_robot)
RobotLab::Web.register(my_robot, name: "support")


35
36
37
# File 'lib/robot_lab/web.rb', line 35

def register(robot, name: nil)
  Registry.register(robot, name: name)
end

.run(robot, message, sink = nil, &block) ⇒ Object

Run robot against message, delivering each lifecycle Event to sink (a callable taking an Event) as it happens, and returning the robot's RobotResult. This is the one call the web routes wrap; it is also the cleanest way to consume the stream from plain Ruby or a test.

Two layers of events arrive at the sink:

* StreamHook turns robot_lab hook moments into :user / :tool_call /
:tool_result / :robot / :error events.
* the streaming block below turns each RubyLLM content chunk into a
:delta event, so the reply can be rendered token by token. (A model or
run that doesn't stream simply produces no deltas — the final :robot
event still carries the whole reply.)

events = []
RobotLab::Web.run(robot, "hello") { |event| events << event }


54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/robot_lab/web.rb', line 54

def run(robot, message, sink = nil, &block)
  sink ||= block
  name = robot.respond_to?(:name) ? robot.name : nil
  EventSink.capture(sink) do
    robot.run(message, hooks: [StreamHook]) do |chunk|
      delta = chunk.respond_to?(:content) ? chunk.content : chunk.to_s
      next if delta.nil? || delta.empty?

      # Emit straight to the sink — deltas bypass the ActivityLog so token
      # spam never floods the dashboard's recent-activity feed.
      EventSink.emit(Event.new(role: :delta, content: delta, robot_name: name))
    end
  end
end