robot_lab 0.3.0 → 0.3.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 (256) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +1 -1
  4. data/lib/robot_lab/mcp/transports/streamable_http.rb +1 -1
  5. data/lib/robot_lab/version.rb +1 -1
  6. metadata +3 -253
  7. data/.architecture/AGENTS.md +0 -32
  8. data/.architecture/config.yml +0 -8
  9. data/.architecture/members.yml +0 -60
  10. data/.architecture/reviews/feature-free-will.md +0 -490
  11. data/.architecture/reviews/overall-codebase.md +0 -427
  12. data/.claude/settings.local.json +0 -57
  13. data/.codex/config.toml +0 -2
  14. data/.envrc +0 -6
  15. data/.github/workflows/deploy-github-pages.yml +0 -52
  16. data/.irbrc +0 -6
  17. data/.loki +0 -65
  18. data/.quality/reek_baseline.txt +0 -43
  19. data/.rubocop.yml +0 -10
  20. data/Archspec.rb +0 -44
  21. data/CLAUDE.md +0 -141
  22. data/COMMITS.md +0 -196
  23. data/Rakefile +0 -193
  24. data/_typos.toml +0 -21
  25. data/agent2agent_review.md +0 -192
  26. data/agentf_improvements.md +0 -253
  27. data/agents.md +0 -14
  28. data/docs/api/core/index.md +0 -147
  29. data/docs/api/core/memory.md +0 -578
  30. data/docs/api/core/network.md +0 -553
  31. data/docs/api/core/result.md +0 -211
  32. data/docs/api/core/robot.md +0 -1667
  33. data/docs/api/core/state.md +0 -145
  34. data/docs/api/core/tool.md +0 -520
  35. data/docs/api/errors.md +0 -323
  36. data/docs/api/hooks.md +0 -469
  37. data/docs/api/index.md +0 -137
  38. data/docs/api/mcp/client.md +0 -387
  39. data/docs/api/mcp/index.md +0 -267
  40. data/docs/api/mcp/server.md +0 -252
  41. data/docs/api/mcp/transports.md +0 -339
  42. data/docs/api/messages/index.md +0 -126
  43. data/docs/api/messages/text-message.md +0 -170
  44. data/docs/api/messages/tool-call-message.md +0 -206
  45. data/docs/api/messages/tool-result-message.md +0 -242
  46. data/docs/api/messages/user-message.md +0 -232
  47. data/docs/api/skills.md +0 -423
  48. data/docs/api/streaming/context.md +0 -280
  49. data/docs/api/streaming/events.md +0 -185
  50. data/docs/api/streaming/index.md +0 -156
  51. data/docs/api/support.md +0 -846
  52. data/docs/architecture/core-concepts.md +0 -575
  53. data/docs/architecture/index.md +0 -252
  54. data/docs/architecture/message-flow.md +0 -366
  55. data/docs/architecture/network-orchestration.md +0 -478
  56. data/docs/architecture/robot-execution.md +0 -396
  57. data/docs/architecture/state-management.md +0 -410
  58. data/docs/assets/css/custom.css +0 -56
  59. data/docs/assets/images/robot_lab.jpg +0 -0
  60. data/docs/concepts.md +0 -641
  61. data/docs/examples/basic-chat.md +0 -258
  62. data/docs/examples/index.md +0 -425
  63. data/docs/examples/mcp-server.md +0 -403
  64. data/docs/examples/multi-robot-network.md +0 -476
  65. data/docs/examples/tool-usage.md +0 -468
  66. data/docs/getting-started/configuration.md +0 -645
  67. data/docs/getting-started/index.md +0 -56
  68. data/docs/getting-started/installation.md +0 -197
  69. data/docs/getting-started/quick-start.md +0 -293
  70. data/docs/guides/building-robots.md +0 -1187
  71. data/docs/guides/creating-networks.md +0 -736
  72. data/docs/guides/hooks.md +0 -1206
  73. data/docs/guides/index.md +0 -78
  74. data/docs/guides/knowledge.md +0 -219
  75. data/docs/guides/mcp-integration.md +0 -740
  76. data/docs/guides/memory.md +0 -458
  77. data/docs/guides/observability.md +0 -733
  78. data/docs/guides/streaming.md +0 -323
  79. data/docs/guides/using-tools.md +0 -780
  80. data/docs/index.md +0 -280
  81. data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
  82. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
  83. data/examples/.envrc +0 -3
  84. data/examples/01_simple_robot.rb +0 -31
  85. data/examples/02_tools.rb +0 -109
  86. data/examples/03_network.rb +0 -118
  87. data/examples/04_mcp.rb +0 -217
  88. data/examples/05_streaming.rb +0 -137
  89. data/examples/06_prompt_templates.rb +0 -325
  90. data/examples/07_network_memory.rb +0 -313
  91. data/examples/08_llm_config.rb +0 -162
  92. data/examples/09_chaining.rb +0 -251
  93. data/examples/10_memory.rb +0 -315
  94. data/examples/11_network_introspection.rb +0 -239
  95. data/examples/12_message_bus.rb +0 -67
  96. data/examples/13_spawn.rb +0 -85
  97. data/examples/14_rusty_circuit/.envrc +0 -1
  98. data/examples/14_rusty_circuit/.gitignore +0 -1
  99. data/examples/14_rusty_circuit/README.md +0 -1
  100. data/examples/14_rusty_circuit/comic.rb +0 -149
  101. data/examples/14_rusty_circuit/display.rb +0 -217
  102. data/examples/14_rusty_circuit/heckler.rb +0 -65
  103. data/examples/14_rusty_circuit/open_mic.rb +0 -132
  104. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
  105. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
  106. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
  107. data/examples/14_rusty_circuit/scout.rb +0 -165
  108. data/examples/14_rusty_circuit/show.log +0 -234
  109. data/examples/15_memory_network_and_bus/.envrc +0 -1
  110. data/examples/15_memory_network_and_bus/README.md +0 -66
  111. data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
  112. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
  113. data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
  114. data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
  115. data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
  116. data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
  117. data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
  118. data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
  119. data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
  120. data/examples/15_memory_network_and_bus/output/memory.json +0 -13
  121. data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
  122. data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
  123. data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
  124. data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
  125. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
  126. data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
  127. data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
  128. data/examples/16_writers_room/.envrc +0 -1
  129. data/examples/16_writers_room/display.rb +0 -158
  130. data/examples/16_writers_room/output/.gitignore +0 -4
  131. data/examples/16_writers_room/output/README.md +0 -69
  132. data/examples/16_writers_room/output/opus_001.md +0 -263
  133. data/examples/16_writers_room/output/opus_001_notes.log +0 -470
  134. data/examples/16_writers_room/output/opus_002.md +0 -245
  135. data/examples/16_writers_room/output/opus_002_notes.log +0 -546
  136. data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
  137. data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
  138. data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
  139. data/examples/16_writers_room/prompts/writer.md +0 -37
  140. data/examples/16_writers_room/room.rb +0 -186
  141. data/examples/16_writers_room/tools.rb +0 -173
  142. data/examples/16_writers_room/writer.rb +0 -121
  143. data/examples/16_writers_room/writers_room.rb +0 -256
  144. data/examples/17_skills.rb +0 -234
  145. data/examples/18_rails/.envrc +0 -3
  146. data/examples/18_rails/.gitignore +0 -5
  147. data/examples/18_rails/Gemfile +0 -11
  148. data/examples/18_rails/README.md +0 -67
  149. data/examples/18_rails/Rakefile +0 -4
  150. data/examples/18_rails/app/controllers/application_controller.rb +0 -4
  151. data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
  152. data/examples/18_rails/app/jobs/application_job.rb +0 -4
  153. data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
  154. data/examples/18_rails/app/models/application_record.rb +0 -5
  155. data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
  156. data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
  157. data/examples/18_rails/app/robots/chat_robot.rb +0 -25
  158. data/examples/18_rails/app/tools/time_tool.rb +0 -9
  159. data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
  160. data/examples/18_rails/app/views/chat/index.html.erb +0 -67
  161. data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
  162. data/examples/18_rails/bin/dev +0 -7
  163. data/examples/18_rails/bin/rails +0 -6
  164. data/examples/18_rails/bin/setup +0 -15
  165. data/examples/18_rails/config/application.rb +0 -33
  166. data/examples/18_rails/config/cable.yml +0 -2
  167. data/examples/18_rails/config/database.yml +0 -5
  168. data/examples/18_rails/config/environment.rb +0 -4
  169. data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
  170. data/examples/18_rails/config/routes.rb +0 -6
  171. data/examples/18_rails/config.ru +0 -4
  172. data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
  173. data/examples/19_token_tracking.rb +0 -138
  174. data/examples/20_circuit_breaker.rb +0 -147
  175. data/examples/21_learning_loop.rb +0 -181
  176. data/examples/22_context_compression.rb +0 -172
  177. data/examples/23_convergence.rb +0 -133
  178. data/examples/24_structured_delegation.rb +0 -151
  179. data/examples/25_history_search/conversation.jsonl +0 -30
  180. data/examples/25_history_search.rb +0 -133
  181. data/examples/26_document_store/api_versioning_adr.md +0 -52
  182. data/examples/26_document_store/incident_postmortem.md +0 -46
  183. data/examples/26_document_store/postgres_runbook.md +0 -49
  184. data/examples/26_document_store/redis_caching_guide.md +0 -48
  185. data/examples/26_document_store/sidekiq_guide.md +0 -51
  186. data/examples/26_document_store.rb +0 -140
  187. data/examples/27_incident_response/README.md +0 -65
  188. data/examples/27_incident_response/incident_response.rb +0 -261
  189. data/examples/28_mcp_discovery.rb +0 -113
  190. data/examples/29_ractor_tools.rb +0 -240
  191. data/examples/30_ractor_network.rb +0 -252
  192. data/examples/31_launch_assessment.rb +0 -246
  193. data/examples/32_newsletter_reader.rb +0 -193
  194. data/examples/33_stock_generator.rb +0 -80
  195. data/examples/33_stock_predictor.rb +0 -327
  196. data/examples/34_agentskills.rb +0 -76
  197. data/examples/35_hooks.rb +0 -266
  198. data/examples/README.md +0 -550
  199. data/examples/common.rb +0 -180
  200. data/examples/prompts/assistant.md +0 -5
  201. data/examples/prompts/audit_trail.md +0 -8
  202. data/examples/prompts/billing.md +0 -10
  203. data/examples/prompts/classifier.md +0 -7
  204. data/examples/prompts/comedian.md +0 -6
  205. data/examples/prompts/comedy_critic.md +0 -10
  206. data/examples/prompts/configurable.md +0 -9
  207. data/examples/prompts/dispatcher.md +0 -12
  208. data/examples/prompts/entity_extractor.md +0 -14
  209. data/examples/prompts/escalation.md +0 -42
  210. data/examples/prompts/frontmatter_mcp_test.md +0 -9
  211. data/examples/prompts/frontmatter_named_test.md +0 -5
  212. data/examples/prompts/frontmatter_tools_test.md +0 -6
  213. data/examples/prompts/general.md +0 -7
  214. data/examples/prompts/github_assistant.md +0 -14
  215. data/examples/prompts/helper.md +0 -4
  216. data/examples/prompts/incident_responder.md +0 -18
  217. data/examples/prompts/keyword_extractor.md +0 -11
  218. data/examples/prompts/llm_config_demo.md +0 -20
  219. data/examples/prompts/order_support.md +0 -35
  220. data/examples/prompts/os_advocate.md +0 -13
  221. data/examples/prompts/os_chief.md +0 -13
  222. data/examples/prompts/os_editor.md +0 -13
  223. data/examples/prompts/parameterized_main_test.md +0 -6
  224. data/examples/prompts/pii_redactor.md +0 -7
  225. data/examples/prompts/product_support.md +0 -37
  226. data/examples/prompts/runbook_protocol.md +0 -12
  227. data/examples/prompts/sentiment_analyzer.md +0 -12
  228. data/examples/prompts/skill_a_test.md +0 -4
  229. data/examples/prompts/skill_b_test.md +0 -4
  230. data/examples/prompts/skill_config_test.md +0 -5
  231. data/examples/prompts/skill_cycle_a_test.md +0 -6
  232. data/examples/prompts/skill_cycle_b_test.md +0 -6
  233. data/examples/prompts/skill_description_test.md +0 -4
  234. data/examples/prompts/skill_leaf_test.md +0 -4
  235. data/examples/prompts/skill_nested_test.md +0 -6
  236. data/examples/prompts/skill_refs_main_test.md +0 -6
  237. data/examples/prompts/skill_self_ref_test.md +0 -6
  238. data/examples/prompts/skill_with_mcp_test.md +0 -9
  239. data/examples/prompts/skill_with_params_test.md +0 -6
  240. data/examples/prompts/skill_with_robot_name_test.md +0 -5
  241. data/examples/prompts/skill_with_tools_test.md +0 -6
  242. data/examples/prompts/sre_compliance.md +0 -10
  243. data/examples/prompts/structured_output.md +0 -7
  244. data/examples/prompts/synthesizer.md +0 -17
  245. data/examples/prompts/technical.md +0 -10
  246. data/examples/prompts/template_with_skills_test.md +0 -6
  247. data/examples/prompts/triage.md +0 -22
  248. data/examples/ruboruby.md +0 -423
  249. data/examples/run_all.rb +0 -60
  250. data/examples/xyzzy.rb +0 -97
  251. data/logfile +0 -8
  252. data/mkdocs.yml +0 -209
  253. data/robot_concurrency.md +0 -38
  254. data/simple_acp_review.md +0 -298
  255. data/temp.md +0 -6
  256. data/tool_manifest_plan.md +0 -155
data/examples/04_mcp.rb DELETED
@@ -1,217 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 4: MCP (Model Context Protocol) Integration with GitHub
5
- #
6
- # Demonstrates connecting to the GitHub MCP server and using its tools
7
- # to interact with GitHub repositories.
8
- #
9
- # Prerequisites:
10
- # 1. Install the GitHub MCP server: brew install github-mcp-server
11
- # 2. Set environment variables:
12
- # - GITHUB_PERSONAL_ACCESS_TOKEN: Your GitHub personal access token
13
- #
14
- # Usage:
15
- # GITHUB_PERSONAL_ACCESS_TOKEN=your_token ruby examples/04_mcp.rb
16
- #
17
- # The GitHub MCP server provides tools for:
18
- # - Searching repositories, code, issues, and users
19
- # - Creating and managing issues and pull requests
20
- # - Reading file contents and repository information
21
- # - Managing branches and commits
22
-
23
- require_relative "common"
24
-
25
- # GitHub MCP server configuration using StdIO transport
26
- github_server = {
27
- name: "github",
28
- transport: {
29
- type: "stdio",
30
- command: "github-mcp-server",
31
- args: ["stdio"],
32
- env: {
33
- "GITHUB_PERSONAL_ACCESS_TOKEN" => ENV.fetch("GITHUB_PERSONAL_ACCESS_TOKEN", "")
34
- }
35
- }
36
- }
37
-
38
- banner "MCP Integration Example: GitHub"
39
-
40
- puts "This example demonstrates using the GitHub MCP server to interact"
41
- puts "with GitHub repositories through the Model Context Protocol."
42
- puts
43
-
44
- # Verify prerequisites
45
- unless ENV["GITHUB_PERSONAL_ACCESS_TOKEN"]
46
- puts <<~ERROR
47
- ERROR: GITHUB_PERSONAL_ACCESS_TOKEN environment variable not set.
48
-
49
- To use this example:
50
- 1. Create a GitHub Personal Access Token at https://github.com/settings/tokens
51
- 2. Grant appropriate permissions (repo, read:user, etc.)
52
- 3. Run: GITHUB_PERSONAL_ACCESS_TOKEN=your_token ruby examples/04_mcp.rb
53
- ERROR
54
- exit 1
55
- end
56
-
57
- # ============================================================================
58
- # Part 1: Direct MCP Client Usage
59
- # ============================================================================
60
-
61
- section "Part 1: Direct MCP Client Usage"
62
- puts "Connecting to GitHub MCP server..."
63
-
64
- begin
65
- # Create MCP client and connect
66
- client = RobotLab::MCP::Client.new(github_server)
67
- client.connect
68
-
69
- unless client.connected?
70
- puts <<~ERROR
71
-
72
- ERROR: Failed to connect to the GitHub MCP server.
73
-
74
- Make sure you have installed it:
75
- brew install github-mcp-server
76
- ERROR
77
- exit 1
78
- end
79
-
80
- puts "Connected successfully!"
81
- puts
82
-
83
- # List available tools
84
- puts "Available GitHub Tools:"
85
- hr
86
-
87
- tools = client.list_tools
88
- if tools.empty?
89
- puts " (No tools returned - check server connection)"
90
- else
91
- tools.each do |tool|
92
- puts " #{tool[:name]}"
93
- puts " #{tool[:description]}" if tool[:description]
94
- end
95
- end
96
-
97
- hr
98
- puts "Total: #{tools.size} tools available"
99
- puts
100
-
101
- # Demonstrate a simple tool call: search for repositories
102
- puts "Demo: Searching for popular Ruby repositories..."
103
- puts
104
-
105
- result = client.call_tool("search_repositories", {
106
- query: "language:ruby stars:>1000",
107
- per_page: 5
108
- })
109
-
110
- # Extract and pretty print the JSON result
111
- if result.is_a?(Array) && result.first.is_a?(Hash) && result.first[:text]
112
- data = JSON.parse(result.first[:text])
113
- puts JSON.pretty_generate(data)
114
- else
115
- puts JSON.pretty_generate(result)
116
- end
117
-
118
- puts
119
-
120
- # Clean up direct client
121
- client.disconnect
122
- puts "Disconnected from direct MCP client."
123
-
124
- # ============================================================================
125
- # Part 2: Robot + MCP Integration
126
- # ============================================================================
127
-
128
-
129
- section "Part 2: Robot + MCP Integration"
130
-
131
- puts "Creating Robot with MCP server integration..."
132
- puts
133
-
134
- # Create a Robot with MCP server - tools are automatically discovered.
135
- # connect_mcp! forces eager connection; without it MCP clients initialize
136
- # lazily on the first run() call, leaving mcp_clients empty until then.
137
- robot = RobotLab.build(
138
- **llm_opts,
139
- name: "github_assistant",
140
- template: :github_assistant,
141
- mcp_servers: [github_server]
142
- )
143
- robot.connect_mcp!
144
-
145
- puts "Robot created: #{robot.name}"
146
- puts " Model: #{robot.model}"
147
- puts " MCP Servers: #{robot.mcp_clients.keys.join(", ")}"
148
- puts " MCP Tools discovered: #{robot.mcp_tools.size}"
149
- puts
150
-
151
- # Show discovered MCP tools
152
- puts "Discovered MCP Tools:"
153
- hr
154
- robot.mcp_tools.first(10).each do |tool|
155
- puts " #{tool.name}"
156
- puts " #{tool.description&.slice(0, 60)}..." if tool.description
157
- end
158
- puts " ... and #{robot.mcp_tools.size - 10} more" if robot.mcp_tools.size > 10
159
- puts
160
-
161
- # Run the robot with a query that will use MCP tools
162
- puts "Running Robot with a GitHub query..."
163
- puts "Query: 'What are the top 3 most starred Ruby web frameworks on GitHub?'"
164
- hr
165
-
166
- # mcp: :inherit is required — run() defaults it to :none, which resolves to
167
- # "no MCP servers this turn" and the discovered tools would never reach the
168
- # provider. tools: takes an allowlist of tool names; :inherit would send all
169
- # ~90 GitHub tools, whose JSON schemas alone exceed 13k tokens and overflow
170
- # a local model's context window (LM Studio's default is 8192) before the
171
- # query even arrives. Send only the one tool this query needs.
172
- result = robot.run(
173
- "What are the top 3 most starred Ruby web frameworks on GitHub? Just list their names and star counts.",
174
- mcp: :inherit,
175
- tools: %w[search_repositories]
176
- )
177
-
178
- puts
179
- puts "Robot Response:"
180
- hr
181
- result.output.each do |msg|
182
- puts msg.content if msg.respond_to?(:content)
183
- end
184
- puts
185
-
186
- # Show tool calls if any were made
187
- if result.tool_calls.any?
188
- puts "Tool Calls Made:"
189
- hr
190
- result.tool_calls.each do |tc|
191
- tool_info = tc.respond_to?(:tool) ? tc.tool : tc
192
- puts " #{tool_info[:name] || tool_info}"
193
- end
194
- puts
195
- end
196
-
197
- # Clean up robot's MCP connections
198
- robot.disconnect
199
- puts "Robot MCP connections disconnected."
200
-
201
- rescue RobotLab::MCPError => e
202
- puts "MCP Error: #{e.message}"
203
- exit 1
204
- rescue Errno::ENOENT
205
- puts <<~ERROR
206
-
207
- ERROR: Could not find the github-mcp-server command.
208
-
209
- Install it with:
210
- brew install github-mcp-server
211
- ERROR
212
- exit 1
213
- end
214
-
215
- puts
216
- hr
217
- puts "Example complete!"
@@ -1,137 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 5: Streaming Content
5
- #
6
- # Demonstrates real-time streaming of LLM responses using:
7
- # 1. Stored callback (on_content:) — wired at build time
8
- # 2. Per-call block — passed to run()
9
- # 3. Both together — stored fires first, then block
10
- #
11
- # Usage:
12
- # ruby examples/05_streaming.rb
13
-
14
- require_relative "common"
15
-
16
- # Send logger output to a file instead of stdout
17
- require 'logger'
18
- log_file = File.join(__dir__, "05.log")
19
- RobotLab.config.logger = Logger.new(log_file)
20
- RubyLLM.configure { |c| c.logger = Logger.new(log_file) }
21
-
22
- banner "Streaming Content"
23
-
24
- # ── 1. Stored callback (on_content:) ─────────────────────────────
25
- #
26
- # Wire streaming at build time. The callback fires on every run() call.
27
-
28
- section "1. Stored Callback (on_content:)"
29
-
30
- chunks_received = 0
31
- robot = RobotLab.build(
32
- **llm_opts,
33
- name: "storyteller",
34
- system_prompt: "You are a concise storyteller. Keep responses under 3 sentences.",
35
- on_content: ->(chunk) {
36
- print chunk.content
37
- chunks_received += 1
38
- }
39
- )
40
-
41
- result = robot.run("Tell me a one-sentence fact about Ruby programming.")
42
-
43
- puts ""
44
- puts "(#{chunks_received} chunks streamed)"
45
- puts ""
46
-
47
- # ── 2. Per-call block ────────────────────────────────────────────
48
- #
49
- # Pass a block to run() for one-off streaming.
50
-
51
- section "2. Per-call Block"
52
-
53
- block_chunks = 0
54
- bare_robot = RobotLab.build(
55
- **llm_opts,
56
- name: "factbot",
57
- system_prompt: "You are concise. Answer in one sentence."
58
- )
59
-
60
- bare_robot.run("What year was Ruby created?") { |chunk|
61
- print chunk.content
62
- block_chunks += 1
63
- }
64
-
65
- puts ""
66
- puts "(#{block_chunks} chunks streamed)"
67
- puts ""
68
-
69
- # ── 3. Both together ────────────────────────────────────────────
70
- #
71
- # When both exist, both fire: stored callback first, then block.
72
-
73
- section "3. Both Together (stored fires first, then block)"
74
-
75
- stored_log = []
76
- block_log = []
77
-
78
- combo_robot = RobotLab.build(
79
- **llm_opts,
80
- name: "combo",
81
- system_prompt: "You are concise. Answer in one sentence.",
82
- on_content: ->(chunk) { stored_log << chunk.content }
83
- )
84
-
85
- combo_robot.run("What is Matz's full name?") { |chunk|
86
- block_log << chunk.content
87
- print chunk.content
88
- }
89
-
90
- puts ""
91
- puts "(stored callback saw #{stored_log.length} chunks, block saw #{block_log.length} chunks)"
92
- puts "Callbacks fired in sync: #{stored_log == block_log}"
93
- puts ""
94
-
95
- # ── 4. Via RunConfig ─────────────────────────────────────────────
96
- #
97
- # on_content participates in the config cascade.
98
-
99
- section "4. Via RunConfig (config cascade)"
100
-
101
- config_chunks = 0
102
- # RunConfig accepts only the fields in RunConfig::FIELDS — `provider` is not
103
- # one of them, so it stays on the robot. on_content is a first-class config
104
- # field and cascades normally.
105
- config = RobotLab::RunConfig.new(
106
- model: LLM[:default].model,
107
- on_content: ->(chunk) {
108
- print chunk.content
109
- config_chunks += 1
110
- }
111
- )
112
-
113
- config_robot = RobotLab.build(
114
- **llm_opts,
115
- name: "config_bot",
116
- system_prompt: "You are concise. Answer in one sentence.",
117
- config: config
118
- )
119
-
120
- config_robot.run("Who designed the Ruby programming language?")
121
-
122
- puts ""
123
- puts "(#{config_chunks} chunks streamed via RunConfig)"
124
- puts ""
125
-
126
- # ── Summary ──────────────────────────────────────────────────────
127
-
128
- section "Summary"
129
- puts <<~SUMMARY
130
- on_content: callback — wired at build time, fires every run()
131
- run() { |chunk| ... } — per-call streaming block
132
- Both together — stored fires first, then block
133
- RunConfig — on_content participates in config cascade
134
-
135
- For structured event streaming (lifecycle events, tool deltas,
136
- sequencing), see RobotLab::Streaming::Context and Events.
137
- SUMMARY
@@ -1,325 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 6: Prompt Templates with prompt_manager
5
- #
6
- # Demonstrates using prompt_manager for organized, reusable prompts
7
- # within a RobotLab network. This example shows an e-commerce support
8
- # system with dynamic context injection using SimpleFlow's optional task routing.
9
- #
10
- # Usage:
11
- # ruby examples/06_prompt_templates.rb
12
- #
13
- # Template Structure (prompt_manager .md files with YAML front matter):
14
- # examples/prompts/
15
- # ├── triage.md
16
- # ├── order_support.md
17
- # ├── product_support.md
18
- # └── escalation.md
19
-
20
- require_relative "common"
21
-
22
- # =============================================================================
23
- # Sample Data
24
- # =============================================================================
25
- # Simulated customer and business data that would come from your database.
26
- # company_name is a template parameter (declared null in all four templates).
27
- # AskUser gathers it from the user before building robots.
28
-
29
- ask = RobotLab::AskUser.new
30
- COMPANY_NAME = ask.call(
31
- "question" => "What is your company name?",
32
- "default" => "TechGear Pro"
33
- )
34
-
35
- SAMPLE_CUSTOMER = {
36
- name: "Sarah Johnson",
37
- email: "sarah.johnson@example.com",
38
- account_type: "Premium",
39
- member_since: "2022-03-15",
40
- vip: true,
41
- lifetime_value: 4_250.00,
42
- recent_orders: [
43
- { id: "ORD-2024-1847", date: "2024-01-10", status: "Delivered", total: 299.99 },
44
- { id: "ORD-2024-1923", date: "2024-01-15", status: "Processing", total: 149.50 }
45
- ],
46
- open_tickets: [
47
- { id: "TKT-5521", subject: "Delayed shipment inquiry", status: "Open" }
48
- ],
49
- purchase_history: [
50
- { category: "Electronics" },
51
- { category: "Accessories" },
52
- { category: "Audio" }
53
- ],
54
- escalation_history: []
55
- }
56
-
57
- CATEGORIES = [
58
- { name: "order", description: "Order status, shipping, returns, refunds" },
59
- { name: "product", description: "Product questions, specifications, recommendations" },
60
- { name: "escalation", description: "Complex issues, complaints, special requests" }
61
- ]
62
-
63
- ORDERS = [
64
- {
65
- id: "ORD-2024-1847",
66
- date: "2024-01-10",
67
- status: "Delivered",
68
- total: 299.99,
69
- tracking: "1Z999AA10123456784",
70
- items: [
71
- { name: "Wireless Noise-Canceling Headphones", quantity: 1, price: 249.99 },
72
- { name: "Premium Carrying Case", quantity: 1, price: 49.99 }
73
- ]
74
- },
75
- {
76
- id: "ORD-2024-1923",
77
- date: "2024-01-15",
78
- status: "Processing",
79
- total: 149.50,
80
- tracking: nil,
81
- items: [
82
- { name: "USB-C Hub Pro", quantity: 1, price: 89.50 },
83
- { name: "Braided USB-C Cable 2m", quantity: 2, price: 30.00 }
84
- ]
85
- }
86
- ]
87
-
88
- PRODUCTS = [
89
- {
90
- name: "Wireless Noise-Canceling Headphones XR500",
91
- sku: "AUDIO-XR500",
92
- price: 249.99,
93
- category: "Audio",
94
- in_stock: true,
95
- quantity: 45,
96
- features: ["40hr battery life", "Active noise cancellation", "Bluetooth 5.2", "Hi-Res Audio"],
97
- compatible_with: ["All Bluetooth devices", "3.5mm audio jack"]
98
- },
99
- {
100
- name: "USB-C Hub Pro 7-in-1",
101
- sku: "ACC-HUB7",
102
- price: 89.50,
103
- category: "Accessories",
104
- in_stock: true,
105
- quantity: 120,
106
- features: ["4K HDMI output", "100W Power Delivery", "SD/MicroSD slots", "USB 3.0 ports"],
107
- compatible_with: ["MacBook Pro", "MacBook Air", "iPad Pro", "Windows laptops"]
108
- }
109
- ]
110
-
111
- PROMOTIONS = [
112
- { name: "New Year Sale", description: "15% off all audio products", code: "AUDIO15" },
113
- { name: "Free Shipping", description: "Free shipping on orders over $75", code: "FREESHIP" }
114
- ]
115
-
116
- PRODUCT_CATEGORIES = [
117
- { name: "Electronics", description: "Laptops, tablets, smartphones" },
118
- { name: "Audio", description: "Headphones, speakers, microphones" },
119
- { name: "Accessories", description: "Cables, hubs, cases, chargers" }
120
- ]
121
-
122
- POLICIES = {
123
- refund_window: 30,
124
- free_shipping_threshold: 75,
125
- express_fee: "$12.99"
126
- }
127
-
128
- ORDER_CAPABILITIES = [
129
- "Check order status and tracking",
130
- "Process returns and exchanges",
131
- "Issue refunds (up to $500 without manager approval)",
132
- "Modify pending orders",
133
- "Apply shipping upgrades"
134
- ]
135
-
136
- ESCALATION_AUTHORITIES = [
137
- { name: "Courtesy Credit", description: "Account credit for inconvenience", limit: "$50" },
138
- { name: "Expedited Shipping", description: "Free upgrade to express shipping", limit: "Unlimited" },
139
- { name: "Extended Return Window", description: "Extend return period", limit: "60 days" },
140
- { name: "Price Match", description: "Match competitor pricing", limit: "20% max discount" }
141
- ]
142
-
143
- # =============================================================================
144
- # Triage Robot with Routing Logic
145
- # =============================================================================
146
-
147
- # Custom triage robot that classifies and activates appropriate specialist
148
- class TriageRobot < RobotLab::Robot
149
- def call(result)
150
- run_context = extract_run_context(result)
151
- message = run_context.delete(:message)
152
- robot_result = run(message, **run_context)
153
-
154
- new_result = result
155
- .with_context(@name.to_sym, robot_result)
156
- .continue(robot_result)
157
-
158
- # Examine LLM output and activate appropriate specialist
159
- classification = robot_result.reply.to_s.strip.downcase
160
-
161
- case classification
162
- when /order/
163
- new_result.activate(:order)
164
- when /product/
165
- new_result.activate(:product)
166
- when /escalat/
167
- new_result.activate(:escalation)
168
- else
169
- # Default to escalation for unclear cases
170
- new_result.activate(:escalation)
171
- end
172
- end
173
- end
174
-
175
- # =============================================================================
176
- # Main Demo
177
- # =============================================================================
178
-
179
- banner "RobotLab + Prompt Templates Demo"
180
- puts "E-Commerce Support Network with Dynamic Context"
181
- puts
182
-
183
- # Template path is set via ROBOT_LAB_TEMPLATE_PATH env var (see top of file)
184
-
185
- # -----------------------------------------------------------------------------
186
- # Build Robots with Template-Based Prompts
187
- # -----------------------------------------------------------------------------
188
-
189
- # Triage Robot - Classifies incoming requests
190
- triage_robot = TriageRobot.new(
191
- name: "triage",
192
- description: "Classifies incoming requests to route to specialists",
193
- template: :triage,
194
- local_tools: [RobotLab::AskUser],
195
- context: {
196
- company_name: COMPANY_NAME,
197
- categories: CATEGORIES
198
- },
199
- **llm_opts
200
- )
201
-
202
- # Order Support Robot
203
- order_robot = RobotLab.build(
204
- **llm_opts,
205
- name: "order",
206
- description: "Handles order-related inquiries with full order history",
207
- template: :order_support,
208
- local_tools: [RobotLab::AskUser],
209
- context: {
210
- company_name: COMPANY_NAME,
211
- policies: POLICIES,
212
- capabilities: ORDER_CAPABILITIES
213
- }
214
- )
215
-
216
- # Product Support Robot
217
- product_robot = RobotLab.build(
218
- **llm_opts,
219
- name: "product",
220
- description: "Answers product questions with catalog knowledge",
221
- template: :product_support,
222
- local_tools: [RobotLab::AskUser],
223
- context: {
224
- company_name: COMPANY_NAME,
225
- products: PRODUCTS,
226
- promotions: PROMOTIONS,
227
- product_categories: PRODUCT_CATEGORIES
228
- }
229
- )
230
-
231
- # Escalation Robot
232
- escalation_robot = RobotLab.build(
233
- **llm_opts,
234
- name: "escalation",
235
- description: "Handles complex cases requiring special authority",
236
- template: :escalation,
237
- local_tools: [RobotLab::AskUser],
238
- context: {
239
- company_name: COMPANY_NAME,
240
- authorities: ESCALATION_AUTHORITIES
241
- }
242
- )
243
-
244
- # -----------------------------------------------------------------------------
245
- # Create Network with Optional Task Routing
246
- # -----------------------------------------------------------------------------
247
-
248
- # tools: :inherit on each task. Task#to_run_params forwards it to robot.run,
249
- # which otherwise defaults to :none and would send the provider an empty tool
250
- # list — the AskUser tool would be attached to each robot but never offered
251
- # to the model.
252
- network = RobotLab.create_network(name: "ecommerce_support") do
253
- task :triage, triage_robot, depends_on: :none, tools: :inherit
254
- task :order, order_robot, depends_on: :optional, tools: :inherit
255
- task :product, product_robot, depends_on: :optional, tools: :inherit
256
- task :escalation, escalation_robot, depends_on: :optional, tools: :inherit
257
- end
258
-
259
- # -----------------------------------------------------------------------------
260
- # Run Demo Scenarios
261
- # -----------------------------------------------------------------------------
262
-
263
- demo_queries = [
264
- {
265
- label: "Order Inquiry",
266
- message: "Where is my order ORD-2024-1923? It's been 5 days and still shows processing."
267
- },
268
- {
269
- label: "Product Question",
270
- message: "Are the XR500 headphones compatible with my iPhone? What's the battery life?"
271
- },
272
- {
273
- label: "Escalation Case",
274
- message: "This is ridiculous! I've been waiting 2 weeks for my order and nobody can help me. I want a refund AND compensation for this terrible experience!"
275
- }
276
- ]
277
-
278
- demo_queries.each_with_index do |query, index|
279
- section "Scenario #{index + 1}: #{query[:label]}"
280
- puts "Customer: #{query[:message]}"
281
- puts
282
-
283
- # Run the network with context
284
- result = network.run(
285
- message: query[:message],
286
- customer: SAMPLE_CUSTOMER,
287
- orders: ORDERS
288
- )
289
-
290
- # Display triage classification
291
- if result.context[:triage]
292
- triage_result = result.context[:triage]
293
- puts "Classification: #{triage_result.reply}"
294
- puts
295
- end
296
-
297
- # Display specialist response (the final value)
298
- if result.value.is_a?(RobotLab::RobotResult)
299
- puts "Routed to: #{result.value.robot_name.upcase}"
300
- content = result.value.reply.to_s
301
- # Truncate long responses for display
302
- if content.length > 300
303
- puts "Response: #{content[0..300]}..."
304
- else
305
- puts "Response: #{content}"
306
- end
307
- end
308
-
309
- puts
310
- hr
311
- end
312
-
313
- puts
314
- puts "Demo Complete!"
315
- puts
316
- puts <<~FOOTER
317
- This example demonstrates:
318
- - prompt_manager templates (.md with YAML front matter)
319
- - Build-time context (robot identity/capabilities)
320
- - Run-time context (customer data, order history)
321
- - Multi-robot network with optional task routing
322
- - SimpleFlow::Result for passing context between robots
323
-
324
- Template files are located in: #{File.join(__dir__, 'prompts')}
325
- FOOTER