robot_lab 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.architecture/AGENTS.md +32 -0
- data/.architecture/config.yml +8 -0
- data/.architecture/members.yml +60 -0
- data/.architecture/reviews/feature-free-will.md +490 -0
- data/.architecture/reviews/overall-codebase.md +427 -0
- data/.claude/settings.local.json +57 -0
- data/.codex/config.toml +2 -0
- data/.irbrc +2 -2
- data/.rubocop.yml +172 -0
- data/CHANGELOG.md +72 -0
- data/CLAUDE.md +139 -0
- data/README.md +91 -95
- data/Rakefile +109 -3
- data/agent2agent_review.md +192 -0
- data/agentf_improvements.md +253 -0
- data/agents.md +14 -0
- data/docs/examples/index.md +37 -2
- data/docs/getting-started/configuration.md +20 -7
- data/docs/guides/index.md +16 -16
- data/docs/guides/knowledge.md +7 -1
- data/docs/guides/observability.md +132 -0
- data/docs/index.md +30 -3
- data/docs/superpowers/plans/2026-05-06-agentskills.md +1303 -0
- data/docs/superpowers/specs/2026-05-06-agentskills-design.md +247 -0
- data/examples/.envrc +1 -0
- data/examples/01_simple_robot.rb +5 -9
- data/examples/02_tools.rb +5 -9
- data/examples/03_network.rb +8 -9
- data/examples/04_mcp.rb +21 -29
- data/examples/05_streaming.rb +12 -18
- data/examples/06_prompt_templates.rb +11 -19
- data/examples/07_network_memory.rb +16 -31
- data/examples/08_llm_config.rb +10 -22
- data/examples/09_chaining.rb +16 -27
- data/examples/10_memory.rb +12 -28
- data/examples/11_network_introspection.rb +15 -29
- data/examples/12_message_bus.rb +5 -12
- data/examples/13_spawn.rb +5 -10
- data/examples/14_rusty_circuit/.envrc +1 -0
- data/examples/14_rusty_circuit/comic.rb +2 -0
- data/examples/14_rusty_circuit/heckler.rb +1 -1
- data/examples/14_rusty_circuit/open_mic.rb +1 -3
- data/examples/14_rusty_circuit/scout.rb +2 -0
- data/examples/15_memory_network_and_bus/.envrc +1 -0
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +6 -3
- data/examples/15_memory_network_and_bus/linux_writer.rb +1 -1
- data/examples/15_memory_network_and_bus/output/combined_article.md +6 -6
- data/examples/15_memory_network_and_bus/output/final_article.md +6 -8
- data/examples/15_memory_network_and_bus/output/linux_draft.md +4 -2
- data/examples/15_memory_network_and_bus/output/mac_draft.md +3 -3
- data/examples/15_memory_network_and_bus/output/memory.json +6 -6
- data/examples/15_memory_network_and_bus/output/revision_1.md +10 -11
- data/examples/15_memory_network_and_bus/output/revision_2.md +6 -8
- data/examples/15_memory_network_and_bus/output/windows_draft.md +3 -3
- data/examples/16_writers_room/.envrc +1 -0
- data/examples/16_writers_room/writers_room.rb +2 -4
- data/examples/17_skills.rb +8 -17
- data/examples/18_rails/Gemfile +1 -0
- data/examples/19_token_tracking.rb +9 -15
- data/examples/20_circuit_breaker.rb +10 -19
- data/examples/21_learning_loop.rb +11 -20
- data/examples/22_context_compression.rb +6 -13
- data/examples/23_convergence.rb +6 -17
- data/examples/24_structured_delegation.rb +11 -15
- data/examples/25_history_search.rb +5 -12
- data/examples/26_document_store.rb +6 -13
- data/examples/27_incident_response/incident_response.rb +4 -5
- data/examples/28_mcp_discovery.rb +8 -11
- data/examples/29_ractor_tools.rb +4 -9
- data/examples/30_ractor_network.rb +10 -19
- data/examples/31_launch_assessment.rb +10 -23
- data/examples/32_newsletter_reader.rb +188 -0
- data/examples/33_stock_generator.rb +80 -0
- data/examples/33_stock_predictor.rb +306 -0
- data/examples/34_agentskills.rb +72 -0
- data/examples/README.md +1 -1
- data/examples/common.rb +76 -0
- data/examples/ruboruby.md +423 -0
- data/examples/temp.md +51 -0
- data/lib/robot_lab/agent_skill.rb +63 -0
- data/lib/robot_lab/agent_skill_catalog.rb +74 -0
- data/lib/robot_lab/ask_user.rb +2 -2
- data/lib/robot_lab/bus_poller.rb +12 -5
- data/lib/robot_lab/config.rb +1 -12
- data/lib/robot_lab/delegation_future.rb +1 -1
- data/lib/robot_lab/doom_loop_detector.rb +98 -0
- data/lib/robot_lab/history_compressor.rb +4 -10
- data/lib/robot_lab/mcp/client.rb +1 -2
- data/lib/robot_lab/mcp/connection_poller.rb +3 -3
- data/lib/robot_lab/mcp/server.rb +1 -1
- data/lib/robot_lab/mcp/server_discovery.rb +0 -2
- data/lib/robot_lab/memory.rb +32 -27
- data/lib/robot_lab/memory_change.rb +2 -2
- data/lib/robot_lab/message.rb +4 -4
- data/lib/robot_lab/network.rb +11 -6
- data/lib/robot_lab/robot/agent_skill_matching.rb +99 -0
- data/lib/robot_lab/robot/bus_messaging.rb +9 -27
- data/lib/robot_lab/robot/history_search.rb +4 -1
- data/lib/robot_lab/robot/mcp_management.rb +5 -11
- data/lib/robot_lab/robot/template_rendering.rb +60 -40
- data/lib/robot_lab/robot.rb +323 -206
- data/lib/robot_lab/robot_result.rb +6 -5
- data/lib/robot_lab/run_config.rb +5 -11
- data/lib/robot_lab/script_tool.rb +76 -0
- data/lib/robot_lab/state_proxy.rb +7 -5
- data/lib/robot_lab/tool.rb +3 -3
- data/lib/robot_lab/tool_config.rb +1 -1
- data/lib/robot_lab/tool_manifest.rb +5 -7
- data/lib/robot_lab/user_message.rb +2 -2
- data/lib/robot_lab/version.rb +1 -1
- data/lib/robot_lab/waiter.rb +1 -1
- data/lib/robot_lab.rb +41 -52
- data/logfile +8 -0
- data/mkdocs.yml +2 -3
- data/robot_concurrency.md +38 -0
- data/simple_acp_review.md +298 -0
- data/site/404.html +2300 -0
- data/site/api/core/index.html +2706 -0
- data/site/api/core/memory/index.html +3793 -0
- data/site/api/core/network/index.html +3500 -0
- data/site/api/core/robot/index.html +4566 -0
- data/site/api/core/state/index.html +3390 -0
- data/site/api/core/tool/index.html +3843 -0
- data/site/api/index.html +2635 -0
- data/site/api/mcp/client/index.html +3435 -0
- data/site/api/mcp/index.html +2783 -0
- data/site/api/mcp/server/index.html +3252 -0
- data/site/api/mcp/transports/index.html +3352 -0
- data/site/api/messages/index.html +2641 -0
- data/site/api/messages/text-message/index.html +3087 -0
- data/site/api/messages/tool-call-message/index.html +3159 -0
- data/site/api/messages/tool-result-message/index.html +3252 -0
- data/site/api/messages/user-message/index.html +3212 -0
- data/site/api/streaming/context/index.html +3282 -0
- data/site/api/streaming/events/index.html +3347 -0
- data/site/api/streaming/index.html +2738 -0
- data/site/architecture/core-concepts/index.html +3757 -0
- data/site/architecture/index.html +2797 -0
- data/site/architecture/message-flow/index.html +3238 -0
- data/site/architecture/network-orchestration/index.html +3433 -0
- data/site/architecture/robot-execution/index.html +3140 -0
- data/site/architecture/state-management/index.html +3498 -0
- data/site/assets/css/custom.css +56 -0
- data/site/assets/images/favicon.png +0 -0
- data/site/assets/images/robot_lab.jpg +0 -0
- data/site/assets/javascripts/bundle.79ae519e.min.js +16 -0
- data/site/assets/javascripts/bundle.79ae519e.min.js.map +7 -0
- data/site/assets/javascripts/lunr/min/lunr.ar.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.da.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.de.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.du.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.el.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.es.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.fi.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.fr.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.he.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.hi.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.hu.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.hy.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.it.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.ja.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.jp.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.kn.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.ko.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.multi.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.nl.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.no.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.pt.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.ro.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.ru.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.sa.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.stemmer.support.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.sv.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.ta.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.te.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.th.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.tr.min.js +18 -0
- data/site/assets/javascripts/lunr/min/lunr.vi.min.js +1 -0
- data/site/assets/javascripts/lunr/min/lunr.zh.min.js +1 -0
- data/site/assets/javascripts/lunr/tinyseg.js +206 -0
- data/site/assets/javascripts/lunr/wordcut.js +6708 -0
- data/site/assets/javascripts/workers/search.2c215733.min.js +42 -0
- data/site/assets/javascripts/workers/search.2c215733.min.js.map +7 -0
- data/site/assets/stylesheets/main.484c7ddc.min.css +1 -0
- data/site/assets/stylesheets/main.484c7ddc.min.css.map +1 -0
- data/site/assets/stylesheets/palette.ab4e12ef.min.css +1 -0
- data/site/assets/stylesheets/palette.ab4e12ef.min.css.map +1 -0
- data/site/concepts/index.html +3455 -0
- data/site/examples/basic-chat/index.html +2880 -0
- data/site/examples/index.html +2907 -0
- data/site/examples/mcp-server/index.html +3018 -0
- data/site/examples/multi-robot-network/index.html +3131 -0
- data/site/examples/rails-application/index.html +3329 -0
- data/site/examples/tool-usage/index.html +3085 -0
- data/site/getting-started/configuration/index.html +3745 -0
- data/site/getting-started/index.html +2572 -0
- data/site/getting-started/installation/index.html +2981 -0
- data/site/getting-started/quick-start/index.html +2942 -0
- data/site/guides/building-robots/index.html +4290 -0
- data/site/guides/creating-networks/index.html +3858 -0
- data/site/guides/index.html +2586 -0
- data/site/guides/mcp-integration/index.html +3581 -0
- data/site/guides/memory/index.html +3586 -0
- data/site/guides/rails-integration/index.html +4019 -0
- data/site/guides/streaming/index.html +3157 -0
- data/site/guides/using-tools/index.html +3802 -0
- data/site/index.html +2671 -0
- data/site/search/search_index.json +1 -0
- data/site/sitemap.xml +183 -0
- data/site/sitemap.xml.gz +0 -0
- data/site/tags.json +1 -0
- data/temp.md +6 -0
- data/tool_manifest_plan.md +155 -0
- metadata +154 -92
- data/docs/examples/rails-application.md +0 -419
- data/docs/guides/ractor-parallelism.md +0 -364
- data/docs/guides/rails-integration.md +0 -681
- data/docs/superpowers/plans/2026-04-14-ractor-integration.md +0 -1538
- data/docs/superpowers/specs/2026-04-14-ractor-integration-design.md +0 -258
- data/lib/generators/robot_lab/install_generator.rb +0 -90
- data/lib/generators/robot_lab/job_generator.rb +0 -40
- data/lib/generators/robot_lab/robot_generator.rb +0 -55
- data/lib/generators/robot_lab/templates/initializer.rb.tt +0 -42
- data/lib/generators/robot_lab/templates/job.rb.tt +0 -21
- data/lib/generators/robot_lab/templates/migration.rb.tt +0 -32
- data/lib/generators/robot_lab/templates/result_model.rb.tt +0 -52
- data/lib/generators/robot_lab/templates/robot.rb.tt +0 -31
- data/lib/generators/robot_lab/templates/robot_job.rb.tt +0 -18
- data/lib/generators/robot_lab/templates/robot_test.rb.tt +0 -34
- data/lib/generators/robot_lab/templates/routing_robot.rb.tt +0 -59
- data/lib/generators/robot_lab/templates/thread_model.rb.tt +0 -40
- data/lib/robot_lab/document_store.rb +0 -155
- data/lib/robot_lab/ractor_boundary.rb +0 -42
- data/lib/robot_lab/ractor_job.rb +0 -37
- data/lib/robot_lab/ractor_memory_proxy.rb +0 -85
- data/lib/robot_lab/ractor_network_scheduler.rb +0 -154
- data/lib/robot_lab/ractor_worker_pool.rb +0 -117
- data/lib/robot_lab/rails_integration/engine.rb +0 -29
- data/lib/robot_lab/rails_integration/job.rb +0 -158
- data/lib/robot_lab/rails_integration/railtie.rb +0 -51
- data/lib/robot_lab/rails_integration/turbo_stream_callbacks.rb +0 -72
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RobotLab
|
|
4
|
-
module RailsIntegration
|
|
5
|
-
# Rails Engine for RobotLab integration
|
|
6
|
-
#
|
|
7
|
-
# Provides automatic loading of RobotLab components and
|
|
8
|
-
# integration with Rails applications.
|
|
9
|
-
#
|
|
10
|
-
class Engine < ::Rails::Engine
|
|
11
|
-
isolate_namespace RobotLab
|
|
12
|
-
|
|
13
|
-
initializer "robot_lab.configure" do |app|
|
|
14
|
-
# Load configuration from Rails config
|
|
15
|
-
app.config.robot_lab ||= ActiveSupport::OrderedOptions.new
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
initializer "robot_lab.add_autoload_paths", before: :set_autoload_paths do |app|
|
|
19
|
-
app.config.autoload_paths << root.join("app", "robots")
|
|
20
|
-
app.config.autoload_paths << root.join("app", "tools")
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
config.generators do |g|
|
|
24
|
-
g.test_framework :minitest, fixture: false
|
|
25
|
-
g.fixture_replacement nil
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RobotLab
|
|
4
|
-
module RailsIntegration
|
|
5
|
-
# Base class for RobotLab background jobs.
|
|
6
|
-
#
|
|
7
|
-
# Encapsulates the full robot-run lifecycle: robot class resolution,
|
|
8
|
-
# Turbo Stream callback wiring, thread-record persistence, and
|
|
9
|
-
# completion/error broadcasting. Suitable for fiber-safe execution
|
|
10
|
-
# under Solid Queue's fiber mode (see issue #20 for SQ version details).
|
|
11
|
-
#
|
|
12
|
-
# @example Minimal subclass using the robot_class DSL
|
|
13
|
-
# class SupportRobotJob < RobotLab::Job
|
|
14
|
-
# queue_as :default
|
|
15
|
-
# robot_class SupportRobot
|
|
16
|
-
# end
|
|
17
|
-
#
|
|
18
|
-
# # Enqueue (no robot_class: needed — taken from DSL):
|
|
19
|
-
# SupportRobotJob.perform_later(message: "Hello", thread_id: session_id)
|
|
20
|
-
#
|
|
21
|
-
# @example Generic job accepting robot_class at enqueue time
|
|
22
|
-
# class RobotRunJob < RobotLab::Job
|
|
23
|
-
# queue_as :default
|
|
24
|
-
# end
|
|
25
|
-
#
|
|
26
|
-
# RobotRunJob.perform_later(
|
|
27
|
-
# robot_class: "SupportRobot",
|
|
28
|
-
# message: params[:message],
|
|
29
|
-
# thread_id: session_id
|
|
30
|
-
# )
|
|
31
|
-
#
|
|
32
|
-
class Job < ActiveJob::Base
|
|
33
|
-
# Set or get the default robot class for this job subclass.
|
|
34
|
-
#
|
|
35
|
-
# @overload robot_class
|
|
36
|
-
# @return [Class, nil] the configured robot class
|
|
37
|
-
# @overload robot_class(klass)
|
|
38
|
-
# @param klass [Class] the robot class (must respond to .build)
|
|
39
|
-
# @return [Class]
|
|
40
|
-
def self.robot_class(klass = nil)
|
|
41
|
-
klass ? @robot_class = klass : @robot_class
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
retry_on StandardError, wait: 5.seconds, attempts: 3
|
|
45
|
-
discard_on ActiveJob::DeserializationError
|
|
46
|
-
|
|
47
|
-
# Run a robot as a background job.
|
|
48
|
-
#
|
|
49
|
-
# When +thread_id+ is provided the job:
|
|
50
|
-
# - Finds or creates a +RobotLabThread+ record and updates its last-message fields
|
|
51
|
-
# - Wires +TurboStreamCallbacks+ onto the robot (when turbo-rails is present)
|
|
52
|
-
# - Persists the +RobotResult+ to +RobotLabResult+
|
|
53
|
-
# - Broadcasts a completion or error event via Turbo Streams
|
|
54
|
-
#
|
|
55
|
-
# When +thread_id+ is omitted the robot runs in a fire-and-forget mode —
|
|
56
|
-
# no persistence, no broadcasting, result returned directly.
|
|
57
|
-
#
|
|
58
|
-
# @param message [String] the user message forwarded to robot.run
|
|
59
|
-
# @param robot_class [String, Class, nil] override; falls back to the class-level DSL
|
|
60
|
-
# @param thread_id [String, nil] session key for persistence and Turbo broadcasting
|
|
61
|
-
# @param context [Hash] additional keyword args forwarded to robot.run
|
|
62
|
-
# @return [RobotResult]
|
|
63
|
-
def perform(message:, robot_class: nil, thread_id: nil, **context)
|
|
64
|
-
klass = resolve_robot_class(robot_class)
|
|
65
|
-
thread = setup_thread(thread_id, message)
|
|
66
|
-
robot = build_robot(klass, thread_id)
|
|
67
|
-
result = robot.run(message, **context)
|
|
68
|
-
|
|
69
|
-
if thread
|
|
70
|
-
persist_result(thread, result)
|
|
71
|
-
broadcast_completion(thread_id)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
result
|
|
75
|
-
rescue StandardError => e
|
|
76
|
-
broadcast_error(thread_id, e) if thread_id
|
|
77
|
-
raise
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
private
|
|
81
|
-
|
|
82
|
-
# Resolve the robot class from the runtime arg or the class-level DSL.
|
|
83
|
-
def resolve_robot_class(runtime_class)
|
|
84
|
-
klass = runtime_class || self.class.robot_class
|
|
85
|
-
raise ArgumentError,
|
|
86
|
-
"No robot class specified. Pass robot_class: to perform or set robot_class on the job class." \
|
|
87
|
-
unless klass
|
|
88
|
-
|
|
89
|
-
return klass if klass.is_a?(Class)
|
|
90
|
-
|
|
91
|
-
klass.to_s.constantize
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# Find or create the thread record and stamp the incoming message.
|
|
95
|
-
# Returns nil when thread_id is absent (fire-and-forget mode).
|
|
96
|
-
def setup_thread(thread_id, message)
|
|
97
|
-
return nil unless thread_id
|
|
98
|
-
|
|
99
|
-
thread = "RobotLabThread".constantize.find_or_create_by_session_id(thread_id)
|
|
100
|
-
thread.update!(last_user_message: message, last_user_message_at: Time.current)
|
|
101
|
-
thread
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# Build the robot, wiring Turbo callbacks when thread_id + turbo-rails are present.
|
|
105
|
-
def build_robot(klass, thread_id)
|
|
106
|
-
if thread_id && turbo_available?
|
|
107
|
-
stream_name = "robot_lab_thread_#{thread_id}"
|
|
108
|
-
on_content = TurboStreamCallbacks.build_content_callback(stream_name: stream_name)
|
|
109
|
-
on_tool_call = TurboStreamCallbacks.build_tool_call_callback(stream_name: stream_name)
|
|
110
|
-
klass.build(on_content: on_content, on_tool_call: on_tool_call)
|
|
111
|
-
else
|
|
112
|
-
klass.build
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
# Append a RobotLabResult record to the thread.
|
|
117
|
-
def persist_result(thread, result)
|
|
118
|
-
sequence = thread.results.maximum(:sequence_number).to_i + 1
|
|
119
|
-
exported = result.export
|
|
120
|
-
|
|
121
|
-
thread.results.create!(
|
|
122
|
-
robot_name: result.robot_name,
|
|
123
|
-
sequence_number: sequence,
|
|
124
|
-
output_data: exported[:output],
|
|
125
|
-
tool_calls_data: exported[:tool_calls],
|
|
126
|
-
stop_reason: result.stop_reason,
|
|
127
|
-
checksum: result.checksum
|
|
128
|
-
)
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
# Broadcast a "Complete" badge to the Turbo Stream.
|
|
132
|
-
def broadcast_completion(thread_id)
|
|
133
|
-
return unless turbo_available?
|
|
134
|
-
|
|
135
|
-
Turbo::StreamsChannel.broadcast_replace_to(
|
|
136
|
-
"robot_lab_thread_#{thread_id}",
|
|
137
|
-
target: "robot_status",
|
|
138
|
-
html: "<div id=\"robot_status\"><span class=\"complete\">Complete</span></div>"
|
|
139
|
-
)
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
# Broadcast an HTML-escaped error message to the Turbo Stream.
|
|
143
|
-
def broadcast_error(thread_id, error)
|
|
144
|
-
return unless turbo_available?
|
|
145
|
-
|
|
146
|
-
Turbo::StreamsChannel.broadcast_append_to(
|
|
147
|
-
"robot_lab_thread_#{thread_id}",
|
|
148
|
-
target: "robot_errors",
|
|
149
|
-
html: "<div class=\"error\">#{ERB::Util.html_escape(error.message)}</div>"
|
|
150
|
-
)
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def turbo_available?
|
|
154
|
-
defined?(Turbo::StreamsChannel)
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
end
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RobotLab
|
|
4
|
-
module RailsIntegration
|
|
5
|
-
# Railtie for RobotLab Rails integration
|
|
6
|
-
#
|
|
7
|
-
# Provides configuration hooks and initialization for
|
|
8
|
-
# Rails applications using RobotLab.
|
|
9
|
-
#
|
|
10
|
-
class Railtie < ::Rails::Railtie
|
|
11
|
-
config.robot_lab = ActiveSupport::OrderedOptions.new
|
|
12
|
-
|
|
13
|
-
initializer "robot_lab.configuration" do |app|
|
|
14
|
-
RobotLab.configure do |config|
|
|
15
|
-
# Apply Rails-specific configuration
|
|
16
|
-
rails_config = app.config.robot_lab
|
|
17
|
-
|
|
18
|
-
config.default_model = rails_config.default_model if rails_config.default_model
|
|
19
|
-
config.default_provider = rails_config.default_provider if rails_config.default_provider
|
|
20
|
-
config.logger = ::Rails.logger
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
initializer "robot_lab.active_record" do
|
|
25
|
-
ActiveSupport.on_load(:active_record) do
|
|
26
|
-
# Extend ActiveRecord with RobotLab concerns if needed
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
rake_tasks do
|
|
31
|
-
# Load RobotLab rake tasks
|
|
32
|
-
path = File.expand_path("../tasks", __dir__)
|
|
33
|
-
Dir.glob("#{path}/**/*.rake").each { |f| load f }
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# TODO: Add fiber isolation warning once Solid Queue fiber mode lands in a
|
|
37
|
-
# mainline release. PR rails/solid_queue#728 (branch crmne/solid_queue
|
|
38
|
-
# async-worker-execution-mode) introduces the `fibers:` worker key but has
|
|
39
|
-
# not yet been merged or released. When a released version is detectable,
|
|
40
|
-
# add an initializer here that warns when:
|
|
41
|
-
# defined?(SolidQueue) &&
|
|
42
|
-
# app.config.active_support.isolation_level != :fiber
|
|
43
|
-
|
|
44
|
-
generators do
|
|
45
|
-
require "generators/robot_lab/install_generator"
|
|
46
|
-
require "generators/robot_lab/robot_generator"
|
|
47
|
-
require "generators/robot_lab/job_generator"
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RobotLab
|
|
4
|
-
module RailsIntegration
|
|
5
|
-
# Stateless utility module that builds callback Procs for Turbo Stream broadcasting.
|
|
6
|
-
#
|
|
7
|
-
# Safe to require even without turbo-rails installed — checks at call time
|
|
8
|
-
# via `defined?(Turbo::StreamsChannel)`.
|
|
9
|
-
#
|
|
10
|
-
# @example Wire streaming in a background job
|
|
11
|
-
# stream_name = "robot_lab_thread_#{thread_id}"
|
|
12
|
-
# on_content = RobotLab::RailsIntegration::TurboStreamCallbacks.build_content_callback(
|
|
13
|
-
# stream_name: stream_name
|
|
14
|
-
# )
|
|
15
|
-
# robot = MyRobot.build(on_content: on_content)
|
|
16
|
-
# robot.run(message)
|
|
17
|
-
#
|
|
18
|
-
module TurboStreamCallbacks
|
|
19
|
-
# Check whether Turbo Streams broadcasting is available at runtime.
|
|
20
|
-
#
|
|
21
|
-
# @return [Boolean]
|
|
22
|
-
#
|
|
23
|
-
def self.available?
|
|
24
|
-
defined?(Turbo::StreamsChannel) ? true : false
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# Build a Proc that broadcasts content chunks via Turbo Streams.
|
|
28
|
-
#
|
|
29
|
-
# The returned Proc receives a chunk object (e.g. RubyLLM::Chunk) and
|
|
30
|
-
# broadcasts `chunk.content` as HTML-escaped text via `broadcast_append_to`.
|
|
31
|
-
#
|
|
32
|
-
# @param stream_name [String] the Turbo Stream channel name
|
|
33
|
-
# @param target [String] the DOM target ID (default: "robot_response")
|
|
34
|
-
# @return [Proc] a callback suitable for `on_content:`
|
|
35
|
-
#
|
|
36
|
-
def self.build_content_callback(stream_name:, target: "robot_response")
|
|
37
|
-
->(chunk) {
|
|
38
|
-
content = chunk.respond_to?(:content) ? chunk.content : chunk.to_s
|
|
39
|
-
return unless content && TurboStreamCallbacks.available?
|
|
40
|
-
|
|
41
|
-
Turbo::StreamsChannel.broadcast_append_to(
|
|
42
|
-
stream_name,
|
|
43
|
-
target: target,
|
|
44
|
-
html: ERB::Util.html_escape(content)
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Build a Proc that broadcasts tool call badges via Turbo Streams.
|
|
50
|
-
#
|
|
51
|
-
# The returned Proc receives a tool_call object and broadcasts
|
|
52
|
-
# a badge showing the tool name.
|
|
53
|
-
#
|
|
54
|
-
# @param stream_name [String] the Turbo Stream channel name
|
|
55
|
-
# @param target [String] the DOM target ID (default: "robot_tools")
|
|
56
|
-
# @return [Proc] a callback suitable for `on_tool_call:`
|
|
57
|
-
#
|
|
58
|
-
def self.build_tool_call_callback(stream_name:, target: "robot_tools")
|
|
59
|
-
->(tool_call) {
|
|
60
|
-
return unless TurboStreamCallbacks.available?
|
|
61
|
-
|
|
62
|
-
name = tool_call.respond_to?(:name) ? tool_call.name : tool_call.to_s
|
|
63
|
-
Turbo::StreamsChannel.broadcast_append_to(
|
|
64
|
-
stream_name,
|
|
65
|
-
target: target,
|
|
66
|
-
html: "<span class=\"tool-badge\">Using: #{ERB::Util.html_escape(name)}</span>"
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|