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.
Files changed (153) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +1 -0
  3. data/.github/workflows/deploy-github-pages.yml +52 -0
  4. data/.github/workflows/deploy-yard-docs.yml +52 -0
  5. data/CHANGELOG.md +55 -0
  6. data/COMMITS.md +196 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +332 -0
  9. data/Rakefile +67 -0
  10. data/docs/api/adapters/anthropic.md +121 -0
  11. data/docs/api/adapters/gemini.md +133 -0
  12. data/docs/api/adapters/index.md +104 -0
  13. data/docs/api/adapters/openai.md +134 -0
  14. data/docs/api/core/index.md +113 -0
  15. data/docs/api/core/memory.md +314 -0
  16. data/docs/api/core/network.md +291 -0
  17. data/docs/api/core/robot.md +273 -0
  18. data/docs/api/core/state.md +273 -0
  19. data/docs/api/core/tool.md +353 -0
  20. data/docs/api/history/active-record-adapter.md +195 -0
  21. data/docs/api/history/config.md +191 -0
  22. data/docs/api/history/index.md +132 -0
  23. data/docs/api/history/thread-manager.md +144 -0
  24. data/docs/api/index.md +82 -0
  25. data/docs/api/mcp/client.md +221 -0
  26. data/docs/api/mcp/index.md +111 -0
  27. data/docs/api/mcp/server.md +225 -0
  28. data/docs/api/mcp/transports.md +264 -0
  29. data/docs/api/messages/index.md +67 -0
  30. data/docs/api/messages/text-message.md +102 -0
  31. data/docs/api/messages/tool-call-message.md +144 -0
  32. data/docs/api/messages/tool-result-message.md +154 -0
  33. data/docs/api/messages/user-message.md +171 -0
  34. data/docs/api/streaming/context.md +174 -0
  35. data/docs/api/streaming/events.md +237 -0
  36. data/docs/api/streaming/index.md +108 -0
  37. data/docs/architecture/core-concepts.md +243 -0
  38. data/docs/architecture/index.md +138 -0
  39. data/docs/architecture/message-flow.md +320 -0
  40. data/docs/architecture/network-orchestration.md +216 -0
  41. data/docs/architecture/robot-execution.md +243 -0
  42. data/docs/architecture/state-management.md +323 -0
  43. data/docs/assets/css/custom.css +56 -0
  44. data/docs/assets/images/robot_lab.jpg +0 -0
  45. data/docs/concepts.md +216 -0
  46. data/docs/examples/basic-chat.md +193 -0
  47. data/docs/examples/index.md +129 -0
  48. data/docs/examples/mcp-server.md +290 -0
  49. data/docs/examples/multi-robot-network.md +312 -0
  50. data/docs/examples/rails-application.md +420 -0
  51. data/docs/examples/tool-usage.md +310 -0
  52. data/docs/getting-started/configuration.md +230 -0
  53. data/docs/getting-started/index.md +56 -0
  54. data/docs/getting-started/installation.md +179 -0
  55. data/docs/getting-started/quick-start.md +203 -0
  56. data/docs/guides/building-robots.md +376 -0
  57. data/docs/guides/creating-networks.md +366 -0
  58. data/docs/guides/history.md +359 -0
  59. data/docs/guides/index.md +68 -0
  60. data/docs/guides/mcp-integration.md +356 -0
  61. data/docs/guides/memory.md +309 -0
  62. data/docs/guides/rails-integration.md +432 -0
  63. data/docs/guides/streaming.md +314 -0
  64. data/docs/guides/using-tools.md +394 -0
  65. data/docs/index.md +160 -0
  66. data/examples/01_simple_robot.rb +38 -0
  67. data/examples/02_tools.rb +106 -0
  68. data/examples/03_network.rb +103 -0
  69. data/examples/04_mcp.rb +219 -0
  70. data/examples/05_streaming.rb +124 -0
  71. data/examples/06_prompt_templates.rb +324 -0
  72. data/examples/07_network_memory.rb +329 -0
  73. data/examples/prompts/assistant/system.txt.erb +2 -0
  74. data/examples/prompts/assistant/user.txt.erb +1 -0
  75. data/examples/prompts/billing/system.txt.erb +7 -0
  76. data/examples/prompts/billing/user.txt.erb +1 -0
  77. data/examples/prompts/classifier/system.txt.erb +4 -0
  78. data/examples/prompts/classifier/user.txt.erb +1 -0
  79. data/examples/prompts/entity_extractor/system.txt.erb +11 -0
  80. data/examples/prompts/entity_extractor/user.txt.erb +3 -0
  81. data/examples/prompts/escalation/system.txt.erb +35 -0
  82. data/examples/prompts/escalation/user.txt.erb +34 -0
  83. data/examples/prompts/general/system.txt.erb +4 -0
  84. data/examples/prompts/general/user.txt.erb +1 -0
  85. data/examples/prompts/github_assistant/system.txt.erb +6 -0
  86. data/examples/prompts/github_assistant/user.txt.erb +1 -0
  87. data/examples/prompts/helper/system.txt.erb +1 -0
  88. data/examples/prompts/helper/user.txt.erb +1 -0
  89. data/examples/prompts/keyword_extractor/system.txt.erb +8 -0
  90. data/examples/prompts/keyword_extractor/user.txt.erb +3 -0
  91. data/examples/prompts/order_support/system.txt.erb +27 -0
  92. data/examples/prompts/order_support/user.txt.erb +22 -0
  93. data/examples/prompts/product_support/system.txt.erb +30 -0
  94. data/examples/prompts/product_support/user.txt.erb +32 -0
  95. data/examples/prompts/sentiment_analyzer/system.txt.erb +9 -0
  96. data/examples/prompts/sentiment_analyzer/user.txt.erb +3 -0
  97. data/examples/prompts/synthesizer/system.txt.erb +14 -0
  98. data/examples/prompts/synthesizer/user.txt.erb +15 -0
  99. data/examples/prompts/technical/system.txt.erb +7 -0
  100. data/examples/prompts/technical/user.txt.erb +1 -0
  101. data/examples/prompts/triage/system.txt.erb +16 -0
  102. data/examples/prompts/triage/user.txt.erb +17 -0
  103. data/lib/generators/robot_lab/install_generator.rb +78 -0
  104. data/lib/generators/robot_lab/robot_generator.rb +55 -0
  105. data/lib/generators/robot_lab/templates/initializer.rb.tt +41 -0
  106. data/lib/generators/robot_lab/templates/migration.rb.tt +32 -0
  107. data/lib/generators/robot_lab/templates/result_model.rb.tt +52 -0
  108. data/lib/generators/robot_lab/templates/robot.rb.tt +46 -0
  109. data/lib/generators/robot_lab/templates/robot_test.rb.tt +32 -0
  110. data/lib/generators/robot_lab/templates/routing_robot.rb.tt +53 -0
  111. data/lib/generators/robot_lab/templates/thread_model.rb.tt +40 -0
  112. data/lib/robot_lab/adapters/anthropic.rb +163 -0
  113. data/lib/robot_lab/adapters/base.rb +85 -0
  114. data/lib/robot_lab/adapters/gemini.rb +193 -0
  115. data/lib/robot_lab/adapters/openai.rb +159 -0
  116. data/lib/robot_lab/adapters/registry.rb +81 -0
  117. data/lib/robot_lab/configuration.rb +143 -0
  118. data/lib/robot_lab/error.rb +32 -0
  119. data/lib/robot_lab/errors.rb +70 -0
  120. data/lib/robot_lab/history/active_record_adapter.rb +146 -0
  121. data/lib/robot_lab/history/config.rb +115 -0
  122. data/lib/robot_lab/history/thread_manager.rb +93 -0
  123. data/lib/robot_lab/mcp/client.rb +210 -0
  124. data/lib/robot_lab/mcp/server.rb +84 -0
  125. data/lib/robot_lab/mcp/transports/base.rb +56 -0
  126. data/lib/robot_lab/mcp/transports/sse.rb +117 -0
  127. data/lib/robot_lab/mcp/transports/stdio.rb +133 -0
  128. data/lib/robot_lab/mcp/transports/streamable_http.rb +139 -0
  129. data/lib/robot_lab/mcp/transports/websocket.rb +108 -0
  130. data/lib/robot_lab/memory.rb +882 -0
  131. data/lib/robot_lab/memory_change.rb +123 -0
  132. data/lib/robot_lab/message.rb +357 -0
  133. data/lib/robot_lab/network.rb +350 -0
  134. data/lib/robot_lab/rails/engine.rb +29 -0
  135. data/lib/robot_lab/rails/railtie.rb +42 -0
  136. data/lib/robot_lab/robot.rb +560 -0
  137. data/lib/robot_lab/robot_result.rb +205 -0
  138. data/lib/robot_lab/robotic_model.rb +324 -0
  139. data/lib/robot_lab/state_proxy.rb +188 -0
  140. data/lib/robot_lab/streaming/context.rb +144 -0
  141. data/lib/robot_lab/streaming/events.rb +95 -0
  142. data/lib/robot_lab/streaming/sequence_counter.rb +48 -0
  143. data/lib/robot_lab/task.rb +117 -0
  144. data/lib/robot_lab/tool.rb +223 -0
  145. data/lib/robot_lab/tool_config.rb +112 -0
  146. data/lib/robot_lab/tool_manifest.rb +234 -0
  147. data/lib/robot_lab/user_message.rb +118 -0
  148. data/lib/robot_lab/version.rb +5 -0
  149. data/lib/robot_lab/waiter.rb +73 -0
  150. data/lib/robot_lab.rb +195 -0
  151. data/mkdocs.yml +214 -0
  152. data/sig/robot_lab.rbs +4 -0
  153. metadata +442 -0
@@ -0,0 +1,273 @@
1
+ # State
2
+
3
+ Manages conversation data, results, and memory.
4
+
5
+ ## Class: `RobotLab::State`
6
+
7
+ ```ruby
8
+ state = RobotLab.create_state(
9
+ message: "Hello",
10
+ data: { user_id: "123" }
11
+ )
12
+ ```
13
+
14
+ ## Attributes
15
+
16
+ ### thread_id
17
+
18
+ ```ruby
19
+ state.thread_id # => String | nil
20
+ ```
21
+
22
+ Conversation thread identifier for persistence.
23
+
24
+ ### memory
25
+
26
+ ```ruby
27
+ state.memory # => Memory
28
+ ```
29
+
30
+ Shared key-value store.
31
+
32
+ ## Methods
33
+
34
+ ### data
35
+
36
+ ```ruby
37
+ state.data # => StateProxy
38
+ ```
39
+
40
+ Access workflow data as a proxy object.
41
+
42
+ ```ruby
43
+ state.data[:user_id] # Hash access
44
+ state.data.user_id # Method access
45
+ state.data[:status] = "active"
46
+ ```
47
+
48
+ ### results
49
+
50
+ ```ruby
51
+ state.results # => Array<RobotResult>
52
+ ```
53
+
54
+ All robot execution results.
55
+
56
+ ### messages
57
+
58
+ ```ruby
59
+ state.messages # => Array<Message>
60
+ ```
61
+
62
+ Formatted conversation messages for LLM.
63
+
64
+ ### append_result
65
+
66
+ ```ruby
67
+ state.append_result(robot_result)
68
+ ```
69
+
70
+ Add a robot result to history.
71
+
72
+ ### set_results
73
+
74
+ ```ruby
75
+ state.set_results(array_of_results)
76
+ ```
77
+
78
+ Replace all results.
79
+
80
+ ### results_from
81
+
82
+ ```ruby
83
+ state.results_from(5) # => Array<RobotResult>
84
+ ```
85
+
86
+ Get results starting at index.
87
+
88
+ ### thread_id=
89
+
90
+ ```ruby
91
+ state.thread_id = "thread_123"
92
+ ```
93
+
94
+ Set the thread identifier.
95
+
96
+ ### format_history
97
+
98
+ ```ruby
99
+ state.format_history # => Array<Message>
100
+ ```
101
+
102
+ Format results as conversation history.
103
+
104
+ ### clone
105
+
106
+ ```ruby
107
+ new_state = state.clone
108
+ ```
109
+
110
+ Create a deep copy.
111
+
112
+ ### to_h
113
+
114
+ ```ruby
115
+ state.to_h # => Hash
116
+ ```
117
+
118
+ Hash representation.
119
+
120
+ ### to_json
121
+
122
+ ```ruby
123
+ state.to_json # => String
124
+ ```
125
+
126
+ JSON representation.
127
+
128
+ ### from_hash (class method)
129
+
130
+ ```ruby
131
+ state = State.from_hash(hash)
132
+ ```
133
+
134
+ Restore from hash.
135
+
136
+ ## StateProxy
137
+
138
+ The `data` attribute is a `StateProxy`:
139
+
140
+ ```ruby
141
+ proxy = state.data
142
+
143
+ # Hash-style access
144
+ proxy[:key]
145
+ proxy[:key] = value
146
+
147
+ # Method-style access
148
+ proxy.key
149
+ proxy.key = value
150
+
151
+ # Hash operations
152
+ proxy.key?(:key)
153
+ proxy.keys
154
+ proxy.values
155
+ proxy.each { |k, v| ... }
156
+ proxy.merge!(other_hash)
157
+ proxy.delete(:key)
158
+ proxy.to_h
159
+ proxy.empty?
160
+ proxy.size
161
+ ```
162
+
163
+ ## Creating State
164
+
165
+ ### Basic
166
+
167
+ ```ruby
168
+ state = RobotLab.create_state(message: "Hello")
169
+ ```
170
+
171
+ ### With Data
172
+
173
+ ```ruby
174
+ state = RobotLab.create_state(
175
+ message: "Process order",
176
+ data: {
177
+ user_id: "user_123",
178
+ order_id: "ord_456"
179
+ }
180
+ )
181
+ ```
182
+
183
+ ### With Thread ID
184
+
185
+ ```ruby
186
+ # Via UserMessage
187
+ message = UserMessage.new("Continue", thread_id: "thread_123")
188
+ state = RobotLab.create_state(message: message)
189
+
190
+ # Direct assignment
191
+ state = RobotLab.create_state(message: "Continue")
192
+ state.thread_id = "thread_123"
193
+ ```
194
+
195
+ ### With Existing Results
196
+
197
+ ```ruby
198
+ state = RobotLab.create_state(
199
+ message: "Follow up",
200
+ results: previous_results
201
+ )
202
+ ```
203
+
204
+ ## UserMessage
205
+
206
+ Enhanced message with metadata:
207
+
208
+ ```ruby
209
+ message = UserMessage.new(
210
+ "What's my order status?",
211
+ thread_id: "thread_123",
212
+ system_prompt: "Respond in Spanish",
213
+ metadata: { source: "web" }
214
+ )
215
+
216
+ message.content # => "What's my order status?"
217
+ message.thread_id # => "thread_123"
218
+ message.system_prompt # => "Respond in Spanish"
219
+ message.metadata # => { source: "web" }
220
+ message.id # => UUID
221
+ message.created_at # => Time
222
+ ```
223
+
224
+ ## Examples
225
+
226
+ ### Accessing Data
227
+
228
+ ```ruby
229
+ state = RobotLab.create_state(
230
+ message: "Help",
231
+ data: { user: { name: "Alice", plan: "pro" } }
232
+ )
233
+
234
+ state.data[:user][:name] # => "Alice"
235
+ state.data.to_h # => { user: { name: "Alice", plan: "pro" } }
236
+ ```
237
+
238
+ ### Working with Results
239
+
240
+ ```ruby
241
+ # After running network
242
+ state.results.size # Number of results
243
+ state.results.last # Most recent
244
+ state.results.map(&:robot_name) # ["classifier", "support"]
245
+ ```
246
+
247
+ ### Using Memory
248
+
249
+ ```ruby
250
+ state.memory.remember("intent", "billing")
251
+ intent = state.memory.recall("intent")
252
+
253
+ scoped = state.memory.scoped("user:123")
254
+ scoped.remember("preference", "dark_mode")
255
+ ```
256
+
257
+ ### Serialization
258
+
259
+ ```ruby
260
+ # Save state
261
+ json = state.to_json
262
+ File.write("state.json", json)
263
+
264
+ # Restore state
265
+ data = JSON.parse(File.read("state.json"))
266
+ state = State.from_hash(data)
267
+ ```
268
+
269
+ ## See Also
270
+
271
+ - [State Management Architecture](../../architecture/state-management.md)
272
+ - [Memory](memory.md)
273
+ - [History Guide](../../guides/history.md)
@@ -0,0 +1,353 @@
1
+ # Tool
2
+
3
+ Callable function that robots can use to interact with external systems.
4
+
5
+ ## Class: `RobotLab::Tool`
6
+
7
+ ```ruby
8
+ tool = RobotLab::Tool.new(
9
+ name: "get_weather",
10
+ description: "Get weather for a location",
11
+ parameters: { location: { type: "string", required: true } },
12
+ handler: ->(location:, **_) { WeatherService.fetch(location) }
13
+ )
14
+ ```
15
+
16
+ ## Constructor
17
+
18
+ ```ruby
19
+ Tool.new(
20
+ name:,
21
+ description: nil,
22
+ parameters: {},
23
+ handler:,
24
+ mcp: false,
25
+ strict: false
26
+ )
27
+ ```
28
+
29
+ **Parameters:**
30
+
31
+ | Name | Type | Description |
32
+ |------|------|-------------|
33
+ | `name` | `String` | Tool identifier |
34
+ | `description` | `String` | What the tool does |
35
+ | `parameters` | `Hash` | Parameter definitions |
36
+ | `handler` | `Proc` | Execution function |
37
+ | `mcp` | `Boolean` | Is this an MCP tool? |
38
+ | `strict` | `Boolean` | Strict parameter validation |
39
+
40
+ ## Attributes
41
+
42
+ ### name
43
+
44
+ ```ruby
45
+ tool.name # => String
46
+ ```
47
+
48
+ Tool identifier.
49
+
50
+ ### description
51
+
52
+ ```ruby
53
+ tool.description # => String
54
+ ```
55
+
56
+ Human-readable description.
57
+
58
+ ### parameters
59
+
60
+ ```ruby
61
+ tool.parameters # => Hash
62
+ ```
63
+
64
+ Parameter schema.
65
+
66
+ ### handler
67
+
68
+ ```ruby
69
+ tool.handler # => Proc
70
+ ```
71
+
72
+ Function that executes the tool.
73
+
74
+ ### mcp
75
+
76
+ ```ruby
77
+ tool.mcp # => Boolean
78
+ ```
79
+
80
+ Whether this is an MCP-sourced tool.
81
+
82
+ ### strict
83
+
84
+ ```ruby
85
+ tool.strict # => Boolean
86
+ ```
87
+
88
+ Whether to use strict parameter validation.
89
+
90
+ ## Methods
91
+
92
+ ### call
93
+
94
+ ```ruby
95
+ result = tool.call(
96
+ params,
97
+ robot: robot,
98
+ network: network,
99
+ state: state
100
+ )
101
+ ```
102
+
103
+ Execute the tool with parameters.
104
+
105
+ ### to_h
106
+
107
+ ```ruby
108
+ tool.to_h # => Hash
109
+ ```
110
+
111
+ Hash representation.
112
+
113
+ ### to_json
114
+
115
+ ```ruby
116
+ tool.to_json # => String
117
+ ```
118
+
119
+ JSON representation.
120
+
121
+ ### to_json_schema
122
+
123
+ ```ruby
124
+ tool.to_json_schema # => Hash
125
+ ```
126
+
127
+ JSON Schema representation for LLM.
128
+
129
+ ## Parameter Schema
130
+
131
+ ### Basic Types
132
+
133
+ ```ruby
134
+ parameters: {
135
+ name: { type: "string" },
136
+ count: { type: "integer" },
137
+ price: { type: "number" },
138
+ active: { type: "boolean" },
139
+ tags: { type: "array" }
140
+ }
141
+ ```
142
+
143
+ ### Required Parameters
144
+
145
+ ```ruby
146
+ parameters: {
147
+ id: { type: "string", required: true }
148
+ }
149
+ ```
150
+
151
+ ### Descriptions
152
+
153
+ ```ruby
154
+ parameters: {
155
+ query: {
156
+ type: "string",
157
+ description: "Search query (supports wildcards)"
158
+ }
159
+ }
160
+ ```
161
+
162
+ ### Enums
163
+
164
+ ```ruby
165
+ parameters: {
166
+ status: {
167
+ type: "string",
168
+ enum: ["pending", "active", "completed"]
169
+ }
170
+ }
171
+ ```
172
+
173
+ ### Defaults
174
+
175
+ ```ruby
176
+ parameters: {
177
+ limit: {
178
+ type: "integer",
179
+ default: 10
180
+ }
181
+ }
182
+ ```
183
+
184
+ ## Handler
185
+
186
+ ### Basic Handler
187
+
188
+ ```ruby
189
+ handler: ->(param:, **_context) {
190
+ do_something(param)
191
+ }
192
+ ```
193
+
194
+ ### With Context
195
+
196
+ ```ruby
197
+ handler: ->(param:, robot:, network:, state:) {
198
+ # robot - The executing Robot
199
+ # network - The NetworkRun
200
+ # state - Current State
201
+
202
+ user = state.data[:user_id]
203
+ state.memory.remember("last_action", param)
204
+
205
+ perform_action(param, user)
206
+ }
207
+ ```
208
+
209
+ ### Error Handling
210
+
211
+ ```ruby
212
+ handler: ->(id:, **_) {
213
+ record = Record.find_by(id: id)
214
+
215
+ if record
216
+ { success: true, data: record.to_h }
217
+ else
218
+ { success: false, error: "Not found" }
219
+ end
220
+ rescue StandardError => e
221
+ { success: false, error: e.message }
222
+ }
223
+ ```
224
+
225
+ ## Builder DSL
226
+
227
+ In robot builder:
228
+
229
+ ```ruby
230
+ tool :tool_name do
231
+ description "What it does"
232
+
233
+ parameter :param1, type: :string, required: true
234
+ parameter :param2, type: :integer, default: 10
235
+
236
+ handler do |param1:, param2:, **_context|
237
+ # Implementation
238
+ end
239
+ end
240
+ ```
241
+
242
+ ## ToolManifest
243
+
244
+ Wrap tools with modified metadata:
245
+
246
+ ```ruby
247
+ manifest = RobotLab::ToolManifest.new(
248
+ tool: original_tool,
249
+ name: "custom_name",
250
+ description: "Custom description"
251
+ )
252
+
253
+ # Original tool is used, metadata is overridden
254
+ ```
255
+
256
+ ### Attributes
257
+
258
+ - `tool` - The wrapped tool
259
+ - `name` - Override name (or original)
260
+ - `description` - Override description (or original)
261
+ - `aliases` - Alternative names
262
+
263
+ ## Examples
264
+
265
+ ### Simple Tool
266
+
267
+ ```ruby
268
+ tool = Tool.new(
269
+ name: "current_time",
270
+ description: "Get the current time",
271
+ handler: ->(**, _) { Time.now.iso8601 }
272
+ )
273
+ ```
274
+
275
+ ### Tool with Parameters
276
+
277
+ ```ruby
278
+ tool = Tool.new(
279
+ name: "search_users",
280
+ description: "Search users by criteria",
281
+ parameters: {
282
+ query: {
283
+ type: "string",
284
+ description: "Search query",
285
+ required: true
286
+ },
287
+ limit: {
288
+ type: "integer",
289
+ description: "Max results",
290
+ default: 10
291
+ },
292
+ status: {
293
+ type: "string",
294
+ enum: ["active", "inactive", "all"],
295
+ default: "active"
296
+ }
297
+ },
298
+ handler: ->(query:, limit:, status:, **_) {
299
+ User.search(query, limit: limit, status: status)
300
+ }
301
+ )
302
+ ```
303
+
304
+ ### API Integration Tool
305
+
306
+ ```ruby
307
+ tool = Tool.new(
308
+ name: "fetch_stock_price",
309
+ description: "Get current stock price",
310
+ parameters: {
311
+ symbol: { type: "string", required: true }
312
+ },
313
+ handler: ->(symbol:, **_) {
314
+ response = HTTP.get("https://api.stocks.example/#{symbol}")
315
+
316
+ if response.status.success?
317
+ JSON.parse(response.body)
318
+ else
319
+ { error: "Failed to fetch", status: response.status.code }
320
+ end
321
+ rescue HTTP::Error => e
322
+ { error: "Network error: #{e.message}" }
323
+ }
324
+ )
325
+ ```
326
+
327
+ ### Database Tool
328
+
329
+ ```ruby
330
+ tool = Tool.new(
331
+ name: "get_order",
332
+ description: "Get order details",
333
+ parameters: {
334
+ order_id: { type: "string", required: true }
335
+ },
336
+ handler: ->(order_id:, state:, **_) {
337
+ user_id = state.data[:user_id]
338
+ order = Order.find_by(id: order_id, user_id: user_id)
339
+
340
+ if order
341
+ order.as_json(include: [:items, :shipping])
342
+ else
343
+ { error: "Order not found or unauthorized" }
344
+ end
345
+ }
346
+ )
347
+ ```
348
+
349
+ ## See Also
350
+
351
+ - [Using Tools Guide](../../guides/using-tools.md)
352
+ - [Robot](robot.md)
353
+ - [MCP Integration](../../guides/mcp-integration.md)