robot_lab 0.0.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 +7 -0
- data/.envrc +1 -0
- data/.github/workflows/deploy-github-pages.yml +52 -0
- data/.github/workflows/deploy-yard-docs.yml +52 -0
- data/CHANGELOG.md +55 -0
- data/COMMITS.md +196 -0
- data/LICENSE.txt +21 -0
- data/README.md +332 -0
- data/Rakefile +67 -0
- data/docs/api/adapters/anthropic.md +121 -0
- data/docs/api/adapters/gemini.md +133 -0
- data/docs/api/adapters/index.md +104 -0
- data/docs/api/adapters/openai.md +134 -0
- data/docs/api/core/index.md +113 -0
- data/docs/api/core/memory.md +314 -0
- data/docs/api/core/network.md +291 -0
- data/docs/api/core/robot.md +273 -0
- data/docs/api/core/state.md +273 -0
- data/docs/api/core/tool.md +353 -0
- data/docs/api/history/active-record-adapter.md +195 -0
- data/docs/api/history/config.md +191 -0
- data/docs/api/history/index.md +132 -0
- data/docs/api/history/thread-manager.md +144 -0
- data/docs/api/index.md +82 -0
- data/docs/api/mcp/client.md +221 -0
- data/docs/api/mcp/index.md +111 -0
- data/docs/api/mcp/server.md +225 -0
- data/docs/api/mcp/transports.md +264 -0
- data/docs/api/messages/index.md +67 -0
- data/docs/api/messages/text-message.md +102 -0
- data/docs/api/messages/tool-call-message.md +144 -0
- data/docs/api/messages/tool-result-message.md +154 -0
- data/docs/api/messages/user-message.md +171 -0
- data/docs/api/streaming/context.md +174 -0
- data/docs/api/streaming/events.md +237 -0
- data/docs/api/streaming/index.md +108 -0
- data/docs/architecture/core-concepts.md +243 -0
- data/docs/architecture/index.md +138 -0
- data/docs/architecture/message-flow.md +320 -0
- data/docs/architecture/network-orchestration.md +216 -0
- data/docs/architecture/robot-execution.md +243 -0
- data/docs/architecture/state-management.md +323 -0
- data/docs/assets/css/custom.css +56 -0
- data/docs/assets/images/robot_lab.jpg +0 -0
- data/docs/concepts.md +216 -0
- data/docs/examples/basic-chat.md +193 -0
- data/docs/examples/index.md +129 -0
- data/docs/examples/mcp-server.md +290 -0
- data/docs/examples/multi-robot-network.md +312 -0
- data/docs/examples/rails-application.md +420 -0
- data/docs/examples/tool-usage.md +310 -0
- data/docs/getting-started/configuration.md +230 -0
- data/docs/getting-started/index.md +56 -0
- data/docs/getting-started/installation.md +179 -0
- data/docs/getting-started/quick-start.md +203 -0
- data/docs/guides/building-robots.md +376 -0
- data/docs/guides/creating-networks.md +366 -0
- data/docs/guides/history.md +359 -0
- data/docs/guides/index.md +68 -0
- data/docs/guides/mcp-integration.md +356 -0
- data/docs/guides/memory.md +309 -0
- data/docs/guides/rails-integration.md +432 -0
- data/docs/guides/streaming.md +314 -0
- data/docs/guides/using-tools.md +394 -0
- data/docs/index.md +160 -0
- data/examples/01_simple_robot.rb +38 -0
- data/examples/02_tools.rb +106 -0
- data/examples/03_network.rb +103 -0
- data/examples/04_mcp.rb +219 -0
- data/examples/05_streaming.rb +124 -0
- data/examples/06_prompt_templates.rb +324 -0
- data/examples/07_network_memory.rb +329 -0
- data/examples/prompts/assistant/system.txt.erb +2 -0
- data/examples/prompts/assistant/user.txt.erb +1 -0
- data/examples/prompts/billing/system.txt.erb +7 -0
- data/examples/prompts/billing/user.txt.erb +1 -0
- data/examples/prompts/classifier/system.txt.erb +4 -0
- data/examples/prompts/classifier/user.txt.erb +1 -0
- data/examples/prompts/entity_extractor/system.txt.erb +11 -0
- data/examples/prompts/entity_extractor/user.txt.erb +3 -0
- data/examples/prompts/escalation/system.txt.erb +35 -0
- data/examples/prompts/escalation/user.txt.erb +34 -0
- data/examples/prompts/general/system.txt.erb +4 -0
- data/examples/prompts/general/user.txt.erb +1 -0
- data/examples/prompts/github_assistant/system.txt.erb +6 -0
- data/examples/prompts/github_assistant/user.txt.erb +1 -0
- data/examples/prompts/helper/system.txt.erb +1 -0
- data/examples/prompts/helper/user.txt.erb +1 -0
- data/examples/prompts/keyword_extractor/system.txt.erb +8 -0
- data/examples/prompts/keyword_extractor/user.txt.erb +3 -0
- data/examples/prompts/order_support/system.txt.erb +27 -0
- data/examples/prompts/order_support/user.txt.erb +22 -0
- data/examples/prompts/product_support/system.txt.erb +30 -0
- data/examples/prompts/product_support/user.txt.erb +32 -0
- data/examples/prompts/sentiment_analyzer/system.txt.erb +9 -0
- data/examples/prompts/sentiment_analyzer/user.txt.erb +3 -0
- data/examples/prompts/synthesizer/system.txt.erb +14 -0
- data/examples/prompts/synthesizer/user.txt.erb +15 -0
- data/examples/prompts/technical/system.txt.erb +7 -0
- data/examples/prompts/technical/user.txt.erb +1 -0
- data/examples/prompts/triage/system.txt.erb +16 -0
- data/examples/prompts/triage/user.txt.erb +17 -0
- data/lib/generators/robot_lab/install_generator.rb +78 -0
- data/lib/generators/robot_lab/robot_generator.rb +55 -0
- data/lib/generators/robot_lab/templates/initializer.rb.tt +41 -0
- data/lib/generators/robot_lab/templates/migration.rb.tt +32 -0
- data/lib/generators/robot_lab/templates/result_model.rb.tt +52 -0
- data/lib/generators/robot_lab/templates/robot.rb.tt +46 -0
- data/lib/generators/robot_lab/templates/robot_test.rb.tt +32 -0
- data/lib/generators/robot_lab/templates/routing_robot.rb.tt +53 -0
- data/lib/generators/robot_lab/templates/thread_model.rb.tt +40 -0
- data/lib/robot_lab/adapters/anthropic.rb +163 -0
- data/lib/robot_lab/adapters/base.rb +85 -0
- data/lib/robot_lab/adapters/gemini.rb +193 -0
- data/lib/robot_lab/adapters/openai.rb +159 -0
- data/lib/robot_lab/adapters/registry.rb +81 -0
- data/lib/robot_lab/configuration.rb +143 -0
- data/lib/robot_lab/error.rb +32 -0
- data/lib/robot_lab/errors.rb +70 -0
- data/lib/robot_lab/history/active_record_adapter.rb +146 -0
- data/lib/robot_lab/history/config.rb +115 -0
- data/lib/robot_lab/history/thread_manager.rb +93 -0
- data/lib/robot_lab/mcp/client.rb +210 -0
- data/lib/robot_lab/mcp/server.rb +84 -0
- data/lib/robot_lab/mcp/transports/base.rb +56 -0
- data/lib/robot_lab/mcp/transports/sse.rb +117 -0
- data/lib/robot_lab/mcp/transports/stdio.rb +133 -0
- data/lib/robot_lab/mcp/transports/streamable_http.rb +139 -0
- data/lib/robot_lab/mcp/transports/websocket.rb +108 -0
- data/lib/robot_lab/memory.rb +882 -0
- data/lib/robot_lab/memory_change.rb +123 -0
- data/lib/robot_lab/message.rb +357 -0
- data/lib/robot_lab/network.rb +350 -0
- data/lib/robot_lab/rails/engine.rb +29 -0
- data/lib/robot_lab/rails/railtie.rb +42 -0
- data/lib/robot_lab/robot.rb +560 -0
- data/lib/robot_lab/robot_result.rb +205 -0
- data/lib/robot_lab/robotic_model.rb +324 -0
- data/lib/robot_lab/state_proxy.rb +188 -0
- data/lib/robot_lab/streaming/context.rb +144 -0
- data/lib/robot_lab/streaming/events.rb +95 -0
- data/lib/robot_lab/streaming/sequence_counter.rb +48 -0
- data/lib/robot_lab/task.rb +117 -0
- data/lib/robot_lab/tool.rb +223 -0
- data/lib/robot_lab/tool_config.rb +112 -0
- data/lib/robot_lab/tool_manifest.rb +234 -0
- data/lib/robot_lab/user_message.rb +118 -0
- data/lib/robot_lab/version.rb +5 -0
- data/lib/robot_lab/waiter.rb +73 -0
- data/lib/robot_lab.rb +195 -0
- data/mkdocs.yml +214 -0
- data/sig/robot_lab.rbs +4 -0
- metadata +442 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
# Rails Integration
|
|
2
|
+
|
|
3
|
+
RobotLab integrates seamlessly with Ruby on Rails applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Generate Files
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
rails generate robot_lab:install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This creates:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
config/initializers/robot_lab.rb # Configuration
|
|
17
|
+
db/migrate/*_create_robot_lab_tables.rb # Database tables
|
|
18
|
+
app/models/robot_lab_thread.rb # Thread model
|
|
19
|
+
app/models/robot_lab_result.rb # Result model
|
|
20
|
+
app/robots/ # Directory for robots
|
|
21
|
+
app/tools/ # Directory for tools
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Run Migrations
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
rails db:migrate
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Configuration
|
|
31
|
+
|
|
32
|
+
### Initializer
|
|
33
|
+
|
|
34
|
+
```ruby title="config/initializers/robot_lab.rb"
|
|
35
|
+
RobotLab.configure do |config|
|
|
36
|
+
# API Keys from credentials
|
|
37
|
+
config.anthropic_api_key = Rails.application.credentials.anthropic_api_key
|
|
38
|
+
config.openai_api_key = Rails.application.credentials.openai_api_key
|
|
39
|
+
|
|
40
|
+
# Defaults
|
|
41
|
+
config.default_provider = :anthropic
|
|
42
|
+
config.default_model = "claude-sonnet-4"
|
|
43
|
+
|
|
44
|
+
# Rails logger
|
|
45
|
+
config.logger = Rails.logger
|
|
46
|
+
|
|
47
|
+
# Template path (auto-configured to app/prompts)
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Environment-Specific
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
RobotLab.configure do |config|
|
|
55
|
+
config.anthropic_api_key = Rails.application.credentials.anthropic_api_key
|
|
56
|
+
|
|
57
|
+
case Rails.env
|
|
58
|
+
when "development"
|
|
59
|
+
config.default_model = "claude-haiku-3" # Faster/cheaper
|
|
60
|
+
config.logger.level = :debug
|
|
61
|
+
when "test"
|
|
62
|
+
config.streaming_enabled = false
|
|
63
|
+
when "production"
|
|
64
|
+
config.default_model = "claude-sonnet-4"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Application Config
|
|
70
|
+
|
|
71
|
+
```ruby title="config/application.rb"
|
|
72
|
+
module MyApp
|
|
73
|
+
class Application < Rails::Application
|
|
74
|
+
config.robot_lab.default_model = "claude-sonnet-4"
|
|
75
|
+
config.robot_lab.default_provider = :anthropic
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Creating Robots
|
|
81
|
+
|
|
82
|
+
### Robot Generator
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
rails generate robot_lab:robot Support
|
|
86
|
+
rails generate robot_lab:robot Billing --description="Handles billing inquiries"
|
|
87
|
+
rails generate robot_lab:robot Router --routing
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Robot Class
|
|
91
|
+
|
|
92
|
+
```ruby title="app/robots/support_robot.rb"
|
|
93
|
+
class SupportRobot
|
|
94
|
+
def self.build
|
|
95
|
+
RobotLab.build do
|
|
96
|
+
name "support"
|
|
97
|
+
description "Handles customer support inquiries"
|
|
98
|
+
model "claude-sonnet-4"
|
|
99
|
+
|
|
100
|
+
template "support/system_prompt"
|
|
101
|
+
|
|
102
|
+
tool :lookup_order do
|
|
103
|
+
description "Look up order by ID"
|
|
104
|
+
parameter :order_id, type: :string, required: true
|
|
105
|
+
handler { |order_id:, **_| Order.find_by(id: order_id)&.to_h }
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Using in Controllers
|
|
113
|
+
|
|
114
|
+
```ruby title="app/controllers/chat_controller.rb"
|
|
115
|
+
class ChatController < ApplicationController
|
|
116
|
+
def create
|
|
117
|
+
network = build_network
|
|
118
|
+
state = RobotLab.create_state(
|
|
119
|
+
message: params[:message],
|
|
120
|
+
data: { user_id: current_user.id }
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
result = network.run(state: state)
|
|
124
|
+
|
|
125
|
+
render json: {
|
|
126
|
+
response: result.last_result.output.first.content,
|
|
127
|
+
thread_id: state.thread_id
|
|
128
|
+
}
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
|
|
133
|
+
def build_network
|
|
134
|
+
RobotLab.create_network do
|
|
135
|
+
name "customer_service"
|
|
136
|
+
add_robot SupportRobot.build
|
|
137
|
+
add_robot BillingRobot.build
|
|
138
|
+
|
|
139
|
+
history history_adapter.to_config
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def history_adapter
|
|
144
|
+
RobotLab::History::ActiveRecordAdapter.new(
|
|
145
|
+
thread_model: RobotLabThread,
|
|
146
|
+
result_model: RobotLabResult
|
|
147
|
+
)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Prompt Templates
|
|
153
|
+
|
|
154
|
+
### Template Location
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
app/prompts/
|
|
158
|
+
├── support/
|
|
159
|
+
│ ├── system_prompt.erb
|
|
160
|
+
│ └── greeting.erb
|
|
161
|
+
└── billing/
|
|
162
|
+
└── system_prompt.erb
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Template Usage
|
|
166
|
+
|
|
167
|
+
```ruby
|
|
168
|
+
robot = RobotLab.build do
|
|
169
|
+
name "support"
|
|
170
|
+
template "support/system_prompt", company: "Acme Corp"
|
|
171
|
+
end
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
```erb title="app/prompts/support/system_prompt.erb"
|
|
175
|
+
You are a support agent for <%= company %>.
|
|
176
|
+
|
|
177
|
+
Your responsibilities:
|
|
178
|
+
- Answer product questions
|
|
179
|
+
- Help with order issues
|
|
180
|
+
- Provide friendly assistance
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Action Cable Integration
|
|
184
|
+
|
|
185
|
+
### Channel
|
|
186
|
+
|
|
187
|
+
```ruby title="app/channels/chat_channel.rb"
|
|
188
|
+
class ChatChannel < ApplicationCable::Channel
|
|
189
|
+
def subscribed
|
|
190
|
+
stream_from "chat_#{params[:thread_id]}"
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def receive(data)
|
|
194
|
+
message = data["message"]
|
|
195
|
+
thread_id = data["thread_id"]
|
|
196
|
+
|
|
197
|
+
state = RobotLab.create_state(message: message)
|
|
198
|
+
state.thread_id = thread_id if thread_id
|
|
199
|
+
|
|
200
|
+
network.run(
|
|
201
|
+
state: state,
|
|
202
|
+
streaming: ->(event) {
|
|
203
|
+
ActionCable.server.broadcast("chat_#{thread_id || state.thread_id}", event)
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
private
|
|
209
|
+
|
|
210
|
+
def network
|
|
211
|
+
@network ||= ChatNetwork.build
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### JavaScript Client
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
const channel = consumer.subscriptions.create(
|
|
220
|
+
{ channel: "ChatChannel", thread_id: threadId },
|
|
221
|
+
{
|
|
222
|
+
received(data) {
|
|
223
|
+
if (data.event === "delta") {
|
|
224
|
+
appendToMessage(data.data.content);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
channel.send({ message: "Hello!", thread_id: threadId });
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Background Jobs
|
|
234
|
+
|
|
235
|
+
### Async Processing
|
|
236
|
+
|
|
237
|
+
```ruby title="app/jobs/process_message_job.rb"
|
|
238
|
+
class ProcessMessageJob < ApplicationJob
|
|
239
|
+
queue_as :default
|
|
240
|
+
|
|
241
|
+
def perform(thread_id:, message:, user_id:)
|
|
242
|
+
state = RobotLab.create_state(
|
|
243
|
+
message: message,
|
|
244
|
+
data: { user_id: user_id }
|
|
245
|
+
)
|
|
246
|
+
state.thread_id = thread_id
|
|
247
|
+
|
|
248
|
+
result = network.run(state: state)
|
|
249
|
+
|
|
250
|
+
# Notify user of completion
|
|
251
|
+
ActionCable.server.broadcast(
|
|
252
|
+
"chat_#{thread_id}",
|
|
253
|
+
{ event: "complete", response: result.last_result.output.first.content }
|
|
254
|
+
)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
private
|
|
258
|
+
|
|
259
|
+
def network
|
|
260
|
+
ChatNetwork.build
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Enqueue from Controller
|
|
266
|
+
|
|
267
|
+
```ruby
|
|
268
|
+
ProcessMessageJob.perform_later(
|
|
269
|
+
thread_id: params[:thread_id],
|
|
270
|
+
message: params[:message],
|
|
271
|
+
user_id: current_user.id
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
render json: { status: "processing" }
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Testing
|
|
278
|
+
|
|
279
|
+
### Test Configuration
|
|
280
|
+
|
|
281
|
+
```ruby title="config/environments/test.rb"
|
|
282
|
+
Rails.application.configure do
|
|
283
|
+
config.robot_lab.streaming_enabled = false
|
|
284
|
+
end
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Robot Tests
|
|
288
|
+
|
|
289
|
+
```ruby title="test/robots/support_robot_test.rb"
|
|
290
|
+
require "test_helper"
|
|
291
|
+
|
|
292
|
+
class SupportRobotTest < ActiveSupport::TestCase
|
|
293
|
+
test "builds valid robot" do
|
|
294
|
+
robot = SupportRobot.build
|
|
295
|
+
assert_equal "support", robot.name
|
|
296
|
+
assert_includes robot.tools.map(&:name), "lookup_order"
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Integration Tests
|
|
302
|
+
|
|
303
|
+
```ruby title="test/integration/chat_test.rb"
|
|
304
|
+
require "test_helper"
|
|
305
|
+
|
|
306
|
+
class ChatTest < ActionDispatch::IntegrationTest
|
|
307
|
+
test "processes chat message" do
|
|
308
|
+
VCR.use_cassette("chat_response") do
|
|
309
|
+
post chat_path, params: { message: "Hello" }
|
|
310
|
+
assert_response :success
|
|
311
|
+
|
|
312
|
+
json = JSON.parse(response.body)
|
|
313
|
+
assert json["response"].present?
|
|
314
|
+
assert json["thread_id"].present?
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Models
|
|
321
|
+
|
|
322
|
+
### Thread Model
|
|
323
|
+
|
|
324
|
+
```ruby title="app/models/robot_lab_thread.rb"
|
|
325
|
+
class RobotLabThread < ApplicationRecord
|
|
326
|
+
has_many :results, class_name: "RobotLabResult", foreign_key: :thread_id
|
|
327
|
+
belongs_to :user, optional: true
|
|
328
|
+
|
|
329
|
+
scope :recent, -> { order(updated_at: :desc) }
|
|
330
|
+
scope :for_user, ->(user) { where(user: user) }
|
|
331
|
+
end
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Result Model
|
|
335
|
+
|
|
336
|
+
```ruby title="app/models/robot_lab_result.rb"
|
|
337
|
+
class RobotLabResult < ApplicationRecord
|
|
338
|
+
belongs_to :thread, class_name: "RobotLabThread"
|
|
339
|
+
|
|
340
|
+
def to_robot_result
|
|
341
|
+
RobotLab::RobotResult.new(
|
|
342
|
+
robot_name: robot_name,
|
|
343
|
+
output: deserialize_messages(output_data),
|
|
344
|
+
tool_calls: deserialize_messages(tool_calls_data),
|
|
345
|
+
stop_reason: stop_reason
|
|
346
|
+
)
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
private
|
|
350
|
+
|
|
351
|
+
def deserialize_messages(data)
|
|
352
|
+
return [] unless data
|
|
353
|
+
data.map { |h| RobotLab::Message.from_hash(h.symbolize_keys) }
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## Best Practices
|
|
359
|
+
|
|
360
|
+
### 1. Use Service Objects
|
|
361
|
+
|
|
362
|
+
```ruby title="app/services/chat_service.rb"
|
|
363
|
+
class ChatService
|
|
364
|
+
def initialize(user:)
|
|
365
|
+
@user = user
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def process(message, thread_id: nil)
|
|
369
|
+
state = build_state(message, thread_id)
|
|
370
|
+
result = network.run(state: state)
|
|
371
|
+
|
|
372
|
+
{
|
|
373
|
+
response: result.last_result.output.first.content,
|
|
374
|
+
thread_id: state.thread_id
|
|
375
|
+
}
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
private
|
|
379
|
+
|
|
380
|
+
def build_state(message, thread_id)
|
|
381
|
+
state = RobotLab.create_state(
|
|
382
|
+
message: message,
|
|
383
|
+
data: { user_id: @user.id }
|
|
384
|
+
)
|
|
385
|
+
state.thread_id = thread_id if thread_id
|
|
386
|
+
state
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def network
|
|
390
|
+
@network ||= ChatNetwork.build
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### 2. Handle Errors
|
|
396
|
+
|
|
397
|
+
```ruby
|
|
398
|
+
def create
|
|
399
|
+
result = ChatService.new(user: current_user).process(params[:message])
|
|
400
|
+
render json: result
|
|
401
|
+
rescue RobotLab::Error => e
|
|
402
|
+
render json: { error: e.message }, status: :unprocessable_entity
|
|
403
|
+
rescue StandardError => e
|
|
404
|
+
Rails.logger.error("Chat error: #{e.message}")
|
|
405
|
+
render json: { error: "An error occurred" }, status: :internal_server_error
|
|
406
|
+
end
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### 3. Rate Limiting
|
|
410
|
+
|
|
411
|
+
```ruby
|
|
412
|
+
class ChatController < ApplicationController
|
|
413
|
+
before_action :check_rate_limit
|
|
414
|
+
|
|
415
|
+
private
|
|
416
|
+
|
|
417
|
+
def check_rate_limit
|
|
418
|
+
key = "chat_rate:#{current_user.id}"
|
|
419
|
+
count = Rails.cache.increment(key, 1, expires_in: 1.minute)
|
|
420
|
+
|
|
421
|
+
if count > 10
|
|
422
|
+
render json: { error: "Rate limit exceeded" }, status: :too_many_requests
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
end
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
## Next Steps
|
|
429
|
+
|
|
430
|
+
- [Building Robots](building-robots.md) - Robot patterns
|
|
431
|
+
- [Creating Networks](creating-networks.md) - Network configuration
|
|
432
|
+
- [History Guide](history.md) - Conversation persistence
|