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,419 +0,0 @@
|
|
|
1
|
-
# Rails Application
|
|
2
|
-
|
|
3
|
-
Full Rails integration with Action Cable and background jobs.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
This example demonstrates integrating RobotLab into a Rails application with real-time streaming via Action Cable, background job processing, and persistent conversation history.
|
|
8
|
-
|
|
9
|
-
## Setup
|
|
10
|
-
|
|
11
|
-
### 1. Add to Gemfile
|
|
12
|
-
|
|
13
|
-
```ruby
|
|
14
|
-
# Gemfile
|
|
15
|
-
gem "robot_lab"
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
### 2. Run Generator
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
rails generate robot_lab:install
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
This creates:
|
|
25
|
-
|
|
26
|
-
- `config/initializers/robot_lab.rb`
|
|
27
|
-
- `app/robots/` directory
|
|
28
|
-
- `app/tools/` directory
|
|
29
|
-
- Database migrations for conversation history
|
|
30
|
-
|
|
31
|
-
### 3. Run Migrations
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
rails db:migrate
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Configuration
|
|
38
|
-
|
|
39
|
-
RobotLab uses MywayConfig for configuration. There is no `RobotLab.configure` block. Instead, configuration is loaded automatically from multiple sources in priority order:
|
|
40
|
-
|
|
41
|
-
1. Bundled defaults (`lib/robot_lab/config/defaults.yml`)
|
|
42
|
-
2. Environment-specific overrides (development, test, production)
|
|
43
|
-
3. XDG user config (`~/.config/robot_lab/config.yml`)
|
|
44
|
-
4. Project config (`./config/robot_lab.yml`)
|
|
45
|
-
5. Environment variables (`ROBOT_LAB_*` prefix)
|
|
46
|
-
|
|
47
|
-
### Config File
|
|
48
|
-
|
|
49
|
-
```yaml
|
|
50
|
-
# config/robot_lab.yml
|
|
51
|
-
defaults:
|
|
52
|
-
ruby_llm:
|
|
53
|
-
model: claude-sonnet-4
|
|
54
|
-
anthropic_api_key: <%= ENV['ANTHROPIC_API_KEY'] %>
|
|
55
|
-
|
|
56
|
-
development:
|
|
57
|
-
ruby_llm:
|
|
58
|
-
log_level: :debug
|
|
59
|
-
|
|
60
|
-
production:
|
|
61
|
-
ruby_llm:
|
|
62
|
-
request_timeout: 180
|
|
63
|
-
max_retries: 5
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Environment Variables
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
# Provider API keys
|
|
70
|
-
ROBOT_LAB_RUBY_LLM__ANTHROPIC_API_KEY=sk-ant-...
|
|
71
|
-
ROBOT_LAB_RUBY_LLM__OPENAI_API_KEY=sk-...
|
|
72
|
-
|
|
73
|
-
# Model configuration
|
|
74
|
-
ROBOT_LAB_RUBY_LLM__MODEL=claude-sonnet-4
|
|
75
|
-
ROBOT_LAB_RUBY_LLM__REQUEST_TIMEOUT=120
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
### Accessing Configuration
|
|
79
|
-
|
|
80
|
-
```ruby
|
|
81
|
-
# Read configuration values at runtime
|
|
82
|
-
RobotLab.config.ruby_llm.model #=> "claude-sonnet-4"
|
|
83
|
-
RobotLab.config.ruby_llm.request_timeout #=> 120
|
|
84
|
-
|
|
85
|
-
# The logger defaults to Rails.logger when running in Rails
|
|
86
|
-
RobotLab.config.logger #=> Rails.logger
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### Rails Engine and Railtie
|
|
90
|
-
|
|
91
|
-
RobotLab provides both a Rails Engine (`RobotLab::RailsIntegration::Engine`) and a Railtie (`RobotLab::RailsIntegration::Railtie`). These are loaded automatically when Rails is detected. The Engine isolates the RobotLab namespace and adds `app/robots` and `app/tools` to the autoload paths. The Railtie loads rake tasks and generators.
|
|
92
|
-
|
|
93
|
-
## Models
|
|
94
|
-
|
|
95
|
-
```ruby
|
|
96
|
-
# app/models/conversation_thread.rb
|
|
97
|
-
class ConversationThread < ApplicationRecord
|
|
98
|
-
belongs_to :user
|
|
99
|
-
has_many :messages, class_name: "ConversationMessage", dependent: :destroy
|
|
100
|
-
|
|
101
|
-
validates :external_id, presence: true, uniqueness: true
|
|
102
|
-
|
|
103
|
-
def self.find_or_create_for(user:, external_id: nil)
|
|
104
|
-
external_id ||= SecureRandom.uuid
|
|
105
|
-
find_or_create_by!(user: user, external_id: external_id)
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# app/models/conversation_message.rb
|
|
110
|
-
class ConversationMessage < ApplicationRecord
|
|
111
|
-
belongs_to :thread, class_name: "ConversationThread"
|
|
112
|
-
|
|
113
|
-
validates :role, presence: true
|
|
114
|
-
validates :content, presence: true
|
|
115
|
-
|
|
116
|
-
scope :ordered, -> { order(:position) }
|
|
117
|
-
end
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Robot Definitions
|
|
121
|
-
|
|
122
|
-
Robots are built using `RobotLab.build` with named parameters. Tools are Ruby classes that inherit from `RubyLLM::Tool`.
|
|
123
|
-
|
|
124
|
-
```ruby
|
|
125
|
-
# app/tools/get_user_info_tool.rb
|
|
126
|
-
class GetUserInfoTool < RubyLLM::Tool
|
|
127
|
-
description "Get information about the current user"
|
|
128
|
-
|
|
129
|
-
param :user_id, type: :integer, desc: "The user ID to look up"
|
|
130
|
-
|
|
131
|
-
def execute(user_id:)
|
|
132
|
-
user = User.find(user_id)
|
|
133
|
-
{
|
|
134
|
-
name: user.name,
|
|
135
|
-
email: user.email,
|
|
136
|
-
plan: user.subscription&.plan || "free",
|
|
137
|
-
member_since: user.created_at.to_date.to_s
|
|
138
|
-
}
|
|
139
|
-
rescue ActiveRecord::RecordNotFound
|
|
140
|
-
{ error: "User not found" }
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# app/tools/get_orders_tool.rb
|
|
145
|
-
class GetOrdersTool < RubyLLM::Tool
|
|
146
|
-
description "Get user's recent orders"
|
|
147
|
-
|
|
148
|
-
param :user_id, type: :integer, desc: "The user ID"
|
|
149
|
-
param :limit, type: :integer, desc: "Number of orders to return", default: 5
|
|
150
|
-
|
|
151
|
-
def execute(user_id:, limit: 5)
|
|
152
|
-
orders = Order.where(user_id: user_id)
|
|
153
|
-
.order(created_at: :desc)
|
|
154
|
-
.limit(limit)
|
|
155
|
-
|
|
156
|
-
orders.map do |order|
|
|
157
|
-
{
|
|
158
|
-
id: order.external_id,
|
|
159
|
-
status: order.status,
|
|
160
|
-
total: order.total.to_f,
|
|
161
|
-
created_at: order.created_at.iso8601
|
|
162
|
-
}
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
# app/tools/create_ticket_tool.rb
|
|
168
|
-
class CreateTicketTool < RubyLLM::Tool
|
|
169
|
-
description "Create a support ticket"
|
|
170
|
-
|
|
171
|
-
param :user_id, type: :integer, desc: "The user ID"
|
|
172
|
-
param :subject, type: :string, desc: "Ticket subject"
|
|
173
|
-
param :description, type: :string, desc: "Ticket description"
|
|
174
|
-
param :priority, type: :string, desc: "Priority level", enum: %w[low medium high]
|
|
175
|
-
|
|
176
|
-
def execute(user_id:, subject:, description:, priority: "medium")
|
|
177
|
-
ticket = SupportTicket.create!(
|
|
178
|
-
user_id: user_id,
|
|
179
|
-
subject: subject,
|
|
180
|
-
description: description,
|
|
181
|
-
priority: priority
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
{
|
|
185
|
-
success: true,
|
|
186
|
-
ticket_id: ticket.external_id,
|
|
187
|
-
message: "Ticket created successfully"
|
|
188
|
-
}
|
|
189
|
-
rescue => e
|
|
190
|
-
{ success: false, error: e.message }
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
```ruby
|
|
196
|
-
# app/robots/support_robot.rb
|
|
197
|
-
class SupportRobot
|
|
198
|
-
def self.build(user_id:)
|
|
199
|
-
RobotLab.build(
|
|
200
|
-
name: "support",
|
|
201
|
-
system_prompt: <<~PROMPT,
|
|
202
|
-
You are a helpful customer support assistant for our company.
|
|
203
|
-
Be friendly, professional, and thorough in your responses.
|
|
204
|
-
If you need to look up information, use the available tools.
|
|
205
|
-
The current user ID is #{user_id}.
|
|
206
|
-
PROMPT
|
|
207
|
-
local_tools: [GetUserInfoTool, GetOrdersTool, CreateTicketTool]
|
|
208
|
-
)
|
|
209
|
-
end
|
|
210
|
-
end
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
## Network Configuration
|
|
214
|
-
|
|
215
|
-
Networks use `create_network` with a block DSL that defines tasks and their dependencies:
|
|
216
|
-
|
|
217
|
-
```ruby
|
|
218
|
-
# app/robots/support_network.rb
|
|
219
|
-
class SupportNetwork
|
|
220
|
-
def self.build(user_id:)
|
|
221
|
-
support = SupportRobot.build(user_id: user_id)
|
|
222
|
-
|
|
223
|
-
RobotLab.create_network(name: "support_network") do
|
|
224
|
-
task :support, support, depends_on: :none
|
|
225
|
-
end
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
## Service Object
|
|
231
|
-
|
|
232
|
-
```ruby
|
|
233
|
-
# app/services/chat_service.rb
|
|
234
|
-
class ChatService
|
|
235
|
-
def initialize(user:, thread_id: nil)
|
|
236
|
-
@user = user
|
|
237
|
-
@thread_id = thread_id
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
def call(message:)
|
|
241
|
-
robot = SupportRobot.build(user_id: @user.id)
|
|
242
|
-
result = robot.run(message)
|
|
243
|
-
|
|
244
|
-
{
|
|
245
|
-
response: result.last_text_content,
|
|
246
|
-
has_tool_calls: result.has_tool_calls?
|
|
247
|
-
}
|
|
248
|
-
end
|
|
249
|
-
end
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
## Controller
|
|
253
|
-
|
|
254
|
-
```ruby
|
|
255
|
-
# app/controllers/api/chats_controller.rb
|
|
256
|
-
module Api
|
|
257
|
-
class ChatsController < ApplicationController
|
|
258
|
-
before_action :authenticate_user!
|
|
259
|
-
|
|
260
|
-
def create
|
|
261
|
-
service = ChatService.new(
|
|
262
|
-
user: current_user,
|
|
263
|
-
thread_id: params[:thread_id]
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
result = service.call(message: params[:message])
|
|
267
|
-
|
|
268
|
-
render json: {
|
|
269
|
-
response: result[:response]
|
|
270
|
-
}
|
|
271
|
-
end
|
|
272
|
-
end
|
|
273
|
-
end
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
## Action Cable Integration
|
|
277
|
-
|
|
278
|
-
```ruby
|
|
279
|
-
# app/channels/chat_channel.rb
|
|
280
|
-
class ChatChannel < ApplicationCable::Channel
|
|
281
|
-
def subscribed
|
|
282
|
-
stream_for current_user
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
def receive(data)
|
|
286
|
-
ChatJob.perform_later(
|
|
287
|
-
user_id: current_user.id,
|
|
288
|
-
thread_id: data["thread_id"],
|
|
289
|
-
message: data["message"]
|
|
290
|
-
)
|
|
291
|
-
end
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
# app/jobs/chat_job.rb
|
|
295
|
-
class ChatJob < ApplicationJob
|
|
296
|
-
queue_as :default
|
|
297
|
-
|
|
298
|
-
def perform(user_id:, thread_id:, message:)
|
|
299
|
-
user = User.find(user_id)
|
|
300
|
-
robot = SupportRobot.build(user_id: user.id)
|
|
301
|
-
|
|
302
|
-
result = robot.run(message)
|
|
303
|
-
|
|
304
|
-
ChatChannel.broadcast_to(
|
|
305
|
-
user,
|
|
306
|
-
type: "complete",
|
|
307
|
-
content: result.last_text_content
|
|
308
|
-
)
|
|
309
|
-
end
|
|
310
|
-
end
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
## Frontend (Stimulus)
|
|
314
|
-
|
|
315
|
-
```javascript
|
|
316
|
-
// app/javascript/controllers/chat_controller.js
|
|
317
|
-
import { Controller } from "@hotwired/stimulus"
|
|
318
|
-
import { createConsumer } from "@rails/actioncable"
|
|
319
|
-
|
|
320
|
-
export default class extends Controller {
|
|
321
|
-
static targets = ["messages", "input", "response"]
|
|
322
|
-
|
|
323
|
-
connect() {
|
|
324
|
-
this.consumer = createConsumer()
|
|
325
|
-
this.channel = this.consumer.subscriptions.create("ChatChannel", {
|
|
326
|
-
received: (data) => this.handleMessage(data)
|
|
327
|
-
})
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
disconnect() {
|
|
331
|
-
this.channel?.unsubscribe()
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
send() {
|
|
335
|
-
const message = this.inputTarget.value.trim()
|
|
336
|
-
if (!message) return
|
|
337
|
-
|
|
338
|
-
this.appendMessage("user", message)
|
|
339
|
-
this.inputTarget.value = ""
|
|
340
|
-
|
|
341
|
-
// Create response container
|
|
342
|
-
this.currentResponse = document.createElement("div")
|
|
343
|
-
this.currentResponse.className = "message assistant"
|
|
344
|
-
this.messagesTarget.appendChild(this.currentResponse)
|
|
345
|
-
|
|
346
|
-
this.channel.send({
|
|
347
|
-
message: message,
|
|
348
|
-
thread_id: this.threadId
|
|
349
|
-
})
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
handleMessage(data) {
|
|
353
|
-
switch (data.type) {
|
|
354
|
-
case "complete":
|
|
355
|
-
this.currentResponse.textContent = data.content
|
|
356
|
-
break
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
appendMessage(role, content) {
|
|
361
|
-
const div = document.createElement("div")
|
|
362
|
-
div.className = `message ${role}`
|
|
363
|
-
div.textContent = content
|
|
364
|
-
this.messagesTarget.appendChild(div)
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
## View
|
|
370
|
-
|
|
371
|
-
```erb
|
|
372
|
-
<!-- app/views/chats/show.html.erb -->
|
|
373
|
-
<div data-controller="chat">
|
|
374
|
-
<div class="messages" data-chat-target="messages">
|
|
375
|
-
<!-- Messages appear here -->
|
|
376
|
-
</div>
|
|
377
|
-
|
|
378
|
-
<form data-action="submit->chat#send">
|
|
379
|
-
<input type="text"
|
|
380
|
-
data-chat-target="input"
|
|
381
|
-
placeholder="Type a message..."
|
|
382
|
-
autocomplete="off">
|
|
383
|
-
<button type="submit">Send</button>
|
|
384
|
-
</form>
|
|
385
|
-
</div>
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
## Running
|
|
389
|
-
|
|
390
|
-
```bash
|
|
391
|
-
# Install dependencies
|
|
392
|
-
bundle install
|
|
393
|
-
yarn install
|
|
394
|
-
|
|
395
|
-
# Setup database
|
|
396
|
-
rails db:migrate
|
|
397
|
-
|
|
398
|
-
# Set API key (or configure via config/robot_lab.yml)
|
|
399
|
-
export ANTHROPIC_API_KEY="your-key"
|
|
400
|
-
|
|
401
|
-
# Start server
|
|
402
|
-
bin/dev
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
## Key Concepts
|
|
406
|
-
|
|
407
|
-
1. **Robot Factory**: `RobotLab.build(name:, system_prompt:, local_tools:, ...)` creates robot instances
|
|
408
|
-
2. **MywayConfig**: Configuration via YAML files and environment variables, not a configure block
|
|
409
|
-
3. **`robot.run("message")`**: Send a message as a positional string argument
|
|
410
|
-
4. **`result.last_text_content`**: Extract the response text from a `RobotResult`
|
|
411
|
-
5. **Memory**: Robots have `robot.memory` for key-value storage; networks share memory
|
|
412
|
-
6. **Tools**: Ruby classes inheriting from `RubyLLM::Tool`, passed via `local_tools:`
|
|
413
|
-
7. **Action Cable**: Real-time streaming to browser
|
|
414
|
-
8. **Background Jobs**: Non-blocking processing
|
|
415
|
-
|
|
416
|
-
## See Also
|
|
417
|
-
|
|
418
|
-
- [Rails Integration Guide](../guides/rails-integration.md)
|
|
419
|
-
- [Streaming Guide](../guides/streaming.md)
|