robot_lab 0.3.0 → 0.5.0

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 (274) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/README.md +29 -19
  4. data/lib/robot_lab/errors.rb +4 -0
  5. data/lib/robot_lab/hook_context.rb +9 -2
  6. data/lib/robot_lab/mcp/registry.rb +99 -0
  7. data/lib/robot_lab/mcp/server.rb +47 -83
  8. data/lib/robot_lab/mcp/server_discovery.rb +22 -9
  9. data/lib/robot_lab/mcp.rb +259 -0
  10. data/lib/robot_lab/robot/mcp_management.rb +159 -105
  11. data/lib/robot_lab/robot/tool_call_gating.rb +144 -0
  12. data/lib/robot_lab/robot.rb +72 -99
  13. data/lib/robot_lab/run_config.rb +3 -2
  14. data/lib/robot_lab/tool.rb +8 -23
  15. data/lib/robot_lab/version.rb +1 -1
  16. data/lib/robot_lab.rb +1 -4
  17. metadata +16 -292
  18. data/.architecture/AGENTS.md +0 -32
  19. data/.architecture/config.yml +0 -8
  20. data/.architecture/members.yml +0 -60
  21. data/.architecture/reviews/feature-free-will.md +0 -490
  22. data/.architecture/reviews/overall-codebase.md +0 -427
  23. data/.claude/settings.local.json +0 -57
  24. data/.codex/config.toml +0 -2
  25. data/.envrc +0 -6
  26. data/.github/workflows/deploy-github-pages.yml +0 -52
  27. data/.irbrc +0 -6
  28. data/.loki +0 -65
  29. data/.quality/reek_baseline.txt +0 -43
  30. data/.rubocop.yml +0 -10
  31. data/Archspec.rb +0 -44
  32. data/CLAUDE.md +0 -141
  33. data/COMMITS.md +0 -196
  34. data/Rakefile +0 -193
  35. data/_typos.toml +0 -21
  36. data/agent2agent_review.md +0 -192
  37. data/agentf_improvements.md +0 -253
  38. data/agents.md +0 -14
  39. data/docs/api/core/index.md +0 -147
  40. data/docs/api/core/memory.md +0 -578
  41. data/docs/api/core/network.md +0 -553
  42. data/docs/api/core/result.md +0 -211
  43. data/docs/api/core/robot.md +0 -1667
  44. data/docs/api/core/state.md +0 -145
  45. data/docs/api/core/tool.md +0 -520
  46. data/docs/api/errors.md +0 -323
  47. data/docs/api/hooks.md +0 -469
  48. data/docs/api/index.md +0 -137
  49. data/docs/api/mcp/client.md +0 -387
  50. data/docs/api/mcp/index.md +0 -267
  51. data/docs/api/mcp/server.md +0 -252
  52. data/docs/api/mcp/transports.md +0 -339
  53. data/docs/api/messages/index.md +0 -126
  54. data/docs/api/messages/text-message.md +0 -170
  55. data/docs/api/messages/tool-call-message.md +0 -206
  56. data/docs/api/messages/tool-result-message.md +0 -242
  57. data/docs/api/messages/user-message.md +0 -232
  58. data/docs/api/skills.md +0 -423
  59. data/docs/api/streaming/context.md +0 -280
  60. data/docs/api/streaming/events.md +0 -185
  61. data/docs/api/streaming/index.md +0 -156
  62. data/docs/api/support.md +0 -846
  63. data/docs/architecture/core-concepts.md +0 -575
  64. data/docs/architecture/index.md +0 -252
  65. data/docs/architecture/message-flow.md +0 -366
  66. data/docs/architecture/network-orchestration.md +0 -478
  67. data/docs/architecture/robot-execution.md +0 -396
  68. data/docs/architecture/state-management.md +0 -410
  69. data/docs/assets/css/custom.css +0 -56
  70. data/docs/assets/images/robot_lab.jpg +0 -0
  71. data/docs/concepts.md +0 -641
  72. data/docs/examples/basic-chat.md +0 -258
  73. data/docs/examples/index.md +0 -425
  74. data/docs/examples/mcp-server.md +0 -403
  75. data/docs/examples/multi-robot-network.md +0 -476
  76. data/docs/examples/tool-usage.md +0 -468
  77. data/docs/getting-started/configuration.md +0 -645
  78. data/docs/getting-started/index.md +0 -56
  79. data/docs/getting-started/installation.md +0 -197
  80. data/docs/getting-started/quick-start.md +0 -293
  81. data/docs/guides/building-robots.md +0 -1187
  82. data/docs/guides/creating-networks.md +0 -736
  83. data/docs/guides/hooks.md +0 -1206
  84. data/docs/guides/index.md +0 -78
  85. data/docs/guides/knowledge.md +0 -219
  86. data/docs/guides/mcp-integration.md +0 -740
  87. data/docs/guides/memory.md +0 -458
  88. data/docs/guides/observability.md +0 -733
  89. data/docs/guides/streaming.md +0 -323
  90. data/docs/guides/using-tools.md +0 -780
  91. data/docs/index.md +0 -280
  92. data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
  93. data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
  94. data/examples/.envrc +0 -3
  95. data/examples/01_simple_robot.rb +0 -31
  96. data/examples/02_tools.rb +0 -109
  97. data/examples/03_network.rb +0 -118
  98. data/examples/04_mcp.rb +0 -217
  99. data/examples/05_streaming.rb +0 -137
  100. data/examples/06_prompt_templates.rb +0 -325
  101. data/examples/07_network_memory.rb +0 -313
  102. data/examples/08_llm_config.rb +0 -162
  103. data/examples/09_chaining.rb +0 -251
  104. data/examples/10_memory.rb +0 -315
  105. data/examples/11_network_introspection.rb +0 -239
  106. data/examples/12_message_bus.rb +0 -67
  107. data/examples/13_spawn.rb +0 -85
  108. data/examples/14_rusty_circuit/.envrc +0 -1
  109. data/examples/14_rusty_circuit/.gitignore +0 -1
  110. data/examples/14_rusty_circuit/README.md +0 -1
  111. data/examples/14_rusty_circuit/comic.rb +0 -149
  112. data/examples/14_rusty_circuit/display.rb +0 -217
  113. data/examples/14_rusty_circuit/heckler.rb +0 -65
  114. data/examples/14_rusty_circuit/open_mic.rb +0 -132
  115. data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
  116. data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
  117. data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
  118. data/examples/14_rusty_circuit/scout.rb +0 -165
  119. data/examples/14_rusty_circuit/show.log +0 -234
  120. data/examples/15_memory_network_and_bus/.envrc +0 -1
  121. data/examples/15_memory_network_and_bus/README.md +0 -66
  122. data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
  123. data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
  124. data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
  125. data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
  126. data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
  127. data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
  128. data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
  129. data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
  130. data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
  131. data/examples/15_memory_network_and_bus/output/memory.json +0 -13
  132. data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
  133. data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
  134. data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
  135. data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
  136. data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
  137. data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
  138. data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
  139. data/examples/16_writers_room/.envrc +0 -1
  140. data/examples/16_writers_room/display.rb +0 -158
  141. data/examples/16_writers_room/output/.gitignore +0 -4
  142. data/examples/16_writers_room/output/README.md +0 -69
  143. data/examples/16_writers_room/output/opus_001.md +0 -263
  144. data/examples/16_writers_room/output/opus_001_notes.log +0 -470
  145. data/examples/16_writers_room/output/opus_002.md +0 -245
  146. data/examples/16_writers_room/output/opus_002_notes.log +0 -546
  147. data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
  148. data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
  149. data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
  150. data/examples/16_writers_room/prompts/writer.md +0 -37
  151. data/examples/16_writers_room/room.rb +0 -186
  152. data/examples/16_writers_room/tools.rb +0 -173
  153. data/examples/16_writers_room/writer.rb +0 -121
  154. data/examples/16_writers_room/writers_room.rb +0 -256
  155. data/examples/17_skills.rb +0 -234
  156. data/examples/18_rails/.envrc +0 -3
  157. data/examples/18_rails/.gitignore +0 -5
  158. data/examples/18_rails/Gemfile +0 -11
  159. data/examples/18_rails/README.md +0 -67
  160. data/examples/18_rails/Rakefile +0 -4
  161. data/examples/18_rails/app/controllers/application_controller.rb +0 -4
  162. data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
  163. data/examples/18_rails/app/jobs/application_job.rb +0 -4
  164. data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
  165. data/examples/18_rails/app/models/application_record.rb +0 -5
  166. data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
  167. data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
  168. data/examples/18_rails/app/robots/chat_robot.rb +0 -25
  169. data/examples/18_rails/app/tools/time_tool.rb +0 -9
  170. data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
  171. data/examples/18_rails/app/views/chat/index.html.erb +0 -67
  172. data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
  173. data/examples/18_rails/bin/dev +0 -7
  174. data/examples/18_rails/bin/rails +0 -6
  175. data/examples/18_rails/bin/setup +0 -15
  176. data/examples/18_rails/config/application.rb +0 -33
  177. data/examples/18_rails/config/cable.yml +0 -2
  178. data/examples/18_rails/config/database.yml +0 -5
  179. data/examples/18_rails/config/environment.rb +0 -4
  180. data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
  181. data/examples/18_rails/config/routes.rb +0 -6
  182. data/examples/18_rails/config.ru +0 -4
  183. data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
  184. data/examples/19_token_tracking.rb +0 -138
  185. data/examples/20_circuit_breaker.rb +0 -147
  186. data/examples/21_learning_loop.rb +0 -181
  187. data/examples/22_context_compression.rb +0 -172
  188. data/examples/23_convergence.rb +0 -133
  189. data/examples/24_structured_delegation.rb +0 -151
  190. data/examples/25_history_search/conversation.jsonl +0 -30
  191. data/examples/25_history_search.rb +0 -133
  192. data/examples/26_document_store/api_versioning_adr.md +0 -52
  193. data/examples/26_document_store/incident_postmortem.md +0 -46
  194. data/examples/26_document_store/postgres_runbook.md +0 -49
  195. data/examples/26_document_store/redis_caching_guide.md +0 -48
  196. data/examples/26_document_store/sidekiq_guide.md +0 -51
  197. data/examples/26_document_store.rb +0 -140
  198. data/examples/27_incident_response/README.md +0 -65
  199. data/examples/27_incident_response/incident_response.rb +0 -261
  200. data/examples/28_mcp_discovery.rb +0 -113
  201. data/examples/29_ractor_tools.rb +0 -240
  202. data/examples/30_ractor_network.rb +0 -252
  203. data/examples/31_launch_assessment.rb +0 -246
  204. data/examples/32_newsletter_reader.rb +0 -193
  205. data/examples/33_stock_generator.rb +0 -80
  206. data/examples/33_stock_predictor.rb +0 -327
  207. data/examples/34_agentskills.rb +0 -76
  208. data/examples/35_hooks.rb +0 -266
  209. data/examples/README.md +0 -550
  210. data/examples/common.rb +0 -180
  211. data/examples/prompts/assistant.md +0 -5
  212. data/examples/prompts/audit_trail.md +0 -8
  213. data/examples/prompts/billing.md +0 -10
  214. data/examples/prompts/classifier.md +0 -7
  215. data/examples/prompts/comedian.md +0 -6
  216. data/examples/prompts/comedy_critic.md +0 -10
  217. data/examples/prompts/configurable.md +0 -9
  218. data/examples/prompts/dispatcher.md +0 -12
  219. data/examples/prompts/entity_extractor.md +0 -14
  220. data/examples/prompts/escalation.md +0 -42
  221. data/examples/prompts/frontmatter_mcp_test.md +0 -9
  222. data/examples/prompts/frontmatter_named_test.md +0 -5
  223. data/examples/prompts/frontmatter_tools_test.md +0 -6
  224. data/examples/prompts/general.md +0 -7
  225. data/examples/prompts/github_assistant.md +0 -14
  226. data/examples/prompts/helper.md +0 -4
  227. data/examples/prompts/incident_responder.md +0 -18
  228. data/examples/prompts/keyword_extractor.md +0 -11
  229. data/examples/prompts/llm_config_demo.md +0 -20
  230. data/examples/prompts/order_support.md +0 -35
  231. data/examples/prompts/os_advocate.md +0 -13
  232. data/examples/prompts/os_chief.md +0 -13
  233. data/examples/prompts/os_editor.md +0 -13
  234. data/examples/prompts/parameterized_main_test.md +0 -6
  235. data/examples/prompts/pii_redactor.md +0 -7
  236. data/examples/prompts/product_support.md +0 -37
  237. data/examples/prompts/runbook_protocol.md +0 -12
  238. data/examples/prompts/sentiment_analyzer.md +0 -12
  239. data/examples/prompts/skill_a_test.md +0 -4
  240. data/examples/prompts/skill_b_test.md +0 -4
  241. data/examples/prompts/skill_config_test.md +0 -5
  242. data/examples/prompts/skill_cycle_a_test.md +0 -6
  243. data/examples/prompts/skill_cycle_b_test.md +0 -6
  244. data/examples/prompts/skill_description_test.md +0 -4
  245. data/examples/prompts/skill_leaf_test.md +0 -4
  246. data/examples/prompts/skill_nested_test.md +0 -6
  247. data/examples/prompts/skill_refs_main_test.md +0 -6
  248. data/examples/prompts/skill_self_ref_test.md +0 -6
  249. data/examples/prompts/skill_with_mcp_test.md +0 -9
  250. data/examples/prompts/skill_with_params_test.md +0 -6
  251. data/examples/prompts/skill_with_robot_name_test.md +0 -5
  252. data/examples/prompts/skill_with_tools_test.md +0 -6
  253. data/examples/prompts/sre_compliance.md +0 -10
  254. data/examples/prompts/structured_output.md +0 -7
  255. data/examples/prompts/synthesizer.md +0 -17
  256. data/examples/prompts/technical.md +0 -10
  257. data/examples/prompts/template_with_skills_test.md +0 -6
  258. data/examples/prompts/triage.md +0 -22
  259. data/examples/ruboruby.md +0 -423
  260. data/examples/run_all.rb +0 -60
  261. data/examples/xyzzy.rb +0 -97
  262. data/lib/robot_lab/mcp/client.rb +0 -223
  263. data/lib/robot_lab/mcp/connection_poller.rb +0 -195
  264. data/lib/robot_lab/mcp/transports/base.rb +0 -64
  265. data/lib/robot_lab/mcp/transports/sse.rb +0 -120
  266. data/lib/robot_lab/mcp/transports/stdio.rb +0 -170
  267. data/lib/robot_lab/mcp/transports/streamable_http.rb +0 -141
  268. data/lib/robot_lab/mcp/transports/websocket.rb +0 -111
  269. data/logfile +0 -8
  270. data/mkdocs.yml +0 -209
  271. data/robot_concurrency.md +0 -38
  272. data/simple_acp_review.md +0 -298
  273. data/temp.md +0 -6
  274. data/tool_manifest_plan.md +0 -155
@@ -1,313 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 7: Network Memory with Concurrent Robots
5
- #
6
- # Demonstrates the reactive shared memory system where:
7
- # - Multiple robots run concurrently and write to shared memory
8
- # - Robots can wait for values written by other robots
9
- # - Subscriptions provide real-time notifications of memory changes
10
- # - Network broadcast sends messages to all robots
11
- #
12
- # Architecture:
13
- # ┌─────────────────────────────────────────────────────────────┐
14
- # │ PARALLEL ANALYSIS │
15
- # │ │
16
- # │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
17
- # │ │ Sentiment │ │ Entities │ │ Keywords │ │
18
- # │ │ Analyzer │ │ Extractor │ │ Extractor │ │
19
- # │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
20
- # │ │ │ │ │
21
- # │ ▼ ▼ ▼ │
22
- # │ memory.set( memory.set( memory.set( │
23
- # │ :sentiment) :entities) :keywords) │
24
- # │ │ │ │ │
25
- # │ └────────────────┼────────────────┘ │
26
- # │ ▼ │
27
- # │ ┌──────────────────────────────────────────────────────┐ │
28
- # │ │ SHARED MEMORY │ │
29
- # │ │ { sentiment: {...}, entities: {...}, keywords: {...} │ │
30
- # │ └──────────────────────────────────────────────────────┘ │
31
- # │ │ │
32
- # │ ▼ │
33
- # │ ┌──────────────────────────────────────────────────────┐ │
34
- # │ │ Synthesizer │ │
35
- # │ │ memory.get(:sentiment, :entities, :keywords, │ │
36
- # │ │ wait: true) │ │
37
- # │ └──────────────────────────────────────────────────────┘ │
38
- # │ │
39
- # └─────────────────────────────────────────────────────────────┘
40
- #
41
- # Usage:
42
- # ruby examples/07_network_memory.rb
43
-
44
- require_relative "common"
45
- require "json"
46
-
47
- banner "Network Memory with Concurrent Robots"
48
-
49
- # -----------------------------------------------------------------------------
50
- # Custom Robot Classes that Write to Shared Memory
51
- # -----------------------------------------------------------------------------
52
-
53
- # Base class for analysis robots that write results to memory
54
- class AnalysisRobot < RobotLab::Robot
55
- def initialize(memory_key:, **opts)
56
- super(**opts)
57
- @memory_key = memory_key
58
- end
59
-
60
- def call(result)
61
- run_context = extract_run_context(result)
62
- network_memory = run_context.delete(:network_memory)
63
- message = run_context.delete(:message)
64
-
65
- robot_result = run(message, network_memory: network_memory, **run_context)
66
-
67
- # Parse the JSON response and write to shared memory
68
- if network_memory
69
- content = robot_result.reply.to_s
70
-
71
- # Set writer before writing to memory
72
- network_memory.current_writer = @name
73
-
74
- begin
75
- parsed = JSON.parse(content)
76
- network_memory.set(@memory_key, parsed)
77
- puts " [#{@name}] Wrote JSON to memory[:#{@memory_key}]"
78
- rescue JSON::ParserError
79
- # If not valid JSON, store the raw text
80
- network_memory.set(@memory_key, content)
81
- puts " [#{@name}] Wrote text to memory[:#{@memory_key}]"
82
- end
83
- end
84
-
85
- result
86
- .with_context(@name.to_sym, robot_result)
87
- .continue(robot_result)
88
- end
89
- end
90
-
91
- # Synthesizer robot that reads from shared memory and synthesizes results
92
- class SynthesizerRobot < RobotLab::Robot
93
- def call(result)
94
- run_context = extract_run_context(result)
95
- network_memory = run_context.delete(:network_memory)
96
- message = run_context.delete(:message)
97
-
98
- puts " [#{@name}] Reading analysis results from memory..."
99
-
100
- if network_memory
101
- # Read results from memory - they should already be there since
102
- # SimpleFlow ensures our dependencies completed first
103
- sentiment = network_memory.get(:sentiment)
104
- entities = network_memory.get(:entities)
105
- keywords = network_memory.get(:keywords)
106
-
107
- puts " [#{@name}] Got sentiment: #{sentiment.nil? ? 'nil' : 'present'}"
108
- puts " [#{@name}] Got entities: #{entities.nil? ? 'nil' : 'present'}"
109
- puts " [#{@name}] Got keywords: #{keywords.nil? ? 'nil' : 'present'}"
110
-
111
- # Format for the template
112
- run_context[:sentiment] = format_for_template(sentiment)
113
- run_context[:entities] = format_for_template(entities)
114
- run_context[:keywords] = format_for_template(keywords)
115
- else
116
- run_context[:sentiment] = "Not available"
117
- run_context[:entities] = "Not available"
118
- run_context[:keywords] = "Not available"
119
- end
120
-
121
- robot_result = run(message, network_memory: network_memory, **run_context)
122
-
123
- result
124
- .with_context(@name.to_sym, robot_result)
125
- .continue(robot_result)
126
- end
127
-
128
- private
129
-
130
- def format_for_template(value)
131
- case value
132
- when Hash, Array
133
- JSON.pretty_generate(value)
134
- when nil
135
- "Not available"
136
- else
137
- value.to_s
138
- end
139
- end
140
- end
141
-
142
- # -----------------------------------------------------------------------------
143
- # Create the Robots
144
- # -----------------------------------------------------------------------------
145
-
146
- sentiment_robot = AnalysisRobot.new(
147
- name: "sentiment_analyzer",
148
- template: :sentiment_analyzer,
149
- memory_key: :sentiment,
150
- **llm_opts
151
- )
152
-
153
- entity_robot = AnalysisRobot.new(
154
- name: "entity_extractor",
155
- template: :entity_extractor,
156
- memory_key: :entities,
157
- **llm_opts
158
- )
159
-
160
- keyword_robot = AnalysisRobot.new(
161
- name: "keyword_extractor",
162
- template: :keyword_extractor,
163
- memory_key: :keywords,
164
- **llm_opts
165
- )
166
-
167
- synthesizer = SynthesizerRobot.new(
168
- name: "synthesizer",
169
- template: :synthesizer,
170
- **llm_opts
171
- )
172
-
173
- # -----------------------------------------------------------------------------
174
- # Create the Network with Shared Memory
175
- # -----------------------------------------------------------------------------
176
-
177
- network = RobotLab.create_network(name: "parallel_analysis") do
178
- # Three analysis robots run in parallel (all depend on nothing)
179
- task :sentiment, sentiment_robot, depends_on: :none
180
- task :entities, entity_robot, depends_on: :none
181
- task :keywords, keyword_robot, depends_on: :none
182
-
183
- # Synthesizer waits for all three to complete
184
- task :synthesize, synthesizer, depends_on: [:sentiment, :entities, :keywords]
185
- end
186
-
187
- # -----------------------------------------------------------------------------
188
- # Set Up Memory Subscriptions (for monitoring)
189
- # -----------------------------------------------------------------------------
190
-
191
- puts "Setting up memory subscriptions for monitoring..."
192
- puts
193
-
194
- # Note: In concurrent execution, change.writer may be unreliable due to race
195
- # conditions. For production use, consider including writer info in the value.
196
- network.memory.subscribe(:sentiment, :entities, :keywords) do |change|
197
- value_preview = case change.value
198
- when Hash then change.value.keys.join(", ")
199
- when String then change.value[0..50]
200
- else change.value.class.name
201
- end
202
- puts " [MONITOR] Memory[:#{change.key}] updated with keys: #{value_preview}"
203
- end
204
-
205
- # -----------------------------------------------------------------------------
206
- # Set Up Broadcast Handler
207
- # -----------------------------------------------------------------------------
208
-
209
- network.on_broadcast do |message|
210
- puts " [BROADCAST] #{message[:payload][:event]}: #{message[:payload][:details]}"
211
- end
212
-
213
- # -----------------------------------------------------------------------------
214
- # Run the Network
215
- # -----------------------------------------------------------------------------
216
-
217
- sample_text = <<~TEXT
218
- Apple Inc. announced today that CEO Tim Cook will be presenting the new iPhone 15
219
- at their headquarters in Cupertino, California on September 12th, 2024.
220
- Industry analysts are extremely excited about the new features, though some
221
- consumer advocates have expressed concerns about the expected price increase.
222
- Samsung and Google are reportedly preparing competitive responses for later this year.
223
- TEXT
224
-
225
- puts "Network structure:"
226
- puts network.visualize
227
- puts
228
- hr
229
- puts "Input text:"
230
- puts sample_text.strip
231
- hr
232
- puts
233
-
234
- # Send a broadcast before starting
235
- network.broadcast(event: "analysis_started", details: "Beginning parallel analysis")
236
-
237
- puts "Running parallel analysis..."
238
- puts
239
- start_time = Time.now
240
-
241
- result = network.run(message: sample_text)
242
-
243
- elapsed = Time.now - start_time
244
- puts
245
- hr
246
- puts "Analysis complete in #{elapsed.round(2)} seconds"
247
- hr
248
-
249
- # Send completion broadcast
250
- network.broadcast(event: "analysis_complete", details: "All robots finished")
251
-
252
- # -----------------------------------------------------------------------------
253
- # Display Results
254
- # -----------------------------------------------------------------------------
255
-
256
-
257
- section "Final Synthesis"
258
-
259
- if result.value.is_a?(RobotLab::RobotResult)
260
- puts result.value.reply
261
- end
262
-
263
-
264
- section "Memory State"
265
-
266
- # Show what's in shared memory
267
- memory = network.memory
268
- puts "Sentiment: #{memory.get(:sentiment)&.to_json}"
269
- puts
270
- puts "Entities: #{memory.get(:entities)&.to_json}"
271
- puts
272
- puts "Keywords: #{memory.get(:keywords)&.to_json}"
273
-
274
- # -----------------------------------------------------------------------------
275
- # Demonstrate Blocking Wait (without pipeline dependencies)
276
- # -----------------------------------------------------------------------------
277
-
278
-
279
- section "Demonstrating Blocking Wait"
280
-
281
- # Create a fresh memory for this demo
282
- demo_memory = RobotLab::Memory.new(network_name: "wait_demo")
283
-
284
- # Simulate a concurrent scenario where one thread waits for another
285
- puts "Starting writer thread (will write after 1 second)..."
286
- puts "Starting reader thread (will wait for value)..."
287
-
288
- reader_result = nil
289
- writer_done = false
290
-
291
- reader = Thread.new do
292
- start = Time.now
293
- puts " [reader] Waiting for :delayed_result..."
294
- value = demo_memory.get(:delayed_result, wait: 10)
295
- elapsed = Time.now - start
296
- reader_result = value
297
- puts " [reader] Got value after #{elapsed.round(2)}s: #{value}"
298
- end
299
-
300
- writer = Thread.new do
301
- sleep 1
302
- demo_memory.set(:delayed_result, { status: "complete", data: [1, 2, 3] })
303
- writer_done = true
304
- puts " [writer] Wrote :delayed_result to memory"
305
- end
306
-
307
- # Wait for both threads
308
- writer.join
309
- reader.join
310
-
311
- puts
312
- puts "Blocking wait demonstration complete!"
313
- puts "Reader received: #{reader_result.inspect}"
@@ -1,162 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Example 8: LLM Configuration via MywayConfig
5
- #
6
- # Demonstrates how RobotLab uses MywayConfig for environment-specific
7
- # configuration (similar to Rails database.yml).
8
- #
9
- # Configuration is loaded from multiple sources in priority order:
10
- # 1. Bundled defaults (lib/robot_lab/config/defaults.yml)
11
- # 2. Environment-specific overrides (development, test, production)
12
- # 3. XDG user config (~/.config/robot_lab/config.yml)
13
- # 4. Project config (./config/robot_lab.yml)
14
- # 5. Environment variables (ROBOT_LAB_*)
15
- # 6. Template front matter (model, temperature, etc. in .md YAML header)
16
- # 7. Constructor parameters (model:, temperature:, etc. passed to Robot.new)
17
- # 8. with_* methods (runtime chaining, e.g. robot.with_temperature(0.9))
18
- # 9. Run-time context (kwargs passed to robot.run that re-render the template)
19
- #
20
- # Environment is determined by ROBOT_LAB_ENV, RAILS_ENV, or RACK_ENV.
21
- #
22
- # Environment variable examples:
23
- # ROBOT_LAB_RUBY_LLM__MODEL=qwen/qwen3.8-27b
24
- # ROBOT_LAB_RUBY_LLM__REQUEST_TIMEOUT=180
25
- # LMS_API_BASE=http://localhost:1234/v1
26
- #
27
- # Usage:
28
- # ruby examples/08_llm_config.rb
29
- # ROBOT_LAB_ENV=test ruby examples/08_llm_config.rb
30
- # ROBOT_LAB_ENV=production ruby examples/08_llm_config.rb
31
-
32
- require_relative "common"
33
-
34
- # =============================================================================
35
- # Demonstration
36
- # =============================================================================
37
-
38
- banner "LLM Configuration via MywayConfig"
39
-
40
- # --- Gather environment from the user via AskUser ---
41
- ask = RobotLab::AskUser.new
42
- env = ask.call(
43
- "question" => "Which environment should the demo run in?",
44
- "choices" => %w[development test production],
45
- "default" => ENV['ROBOT_LAB_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development"
46
- )
47
- puts
48
-
49
- # Display configuration values (hiding sensitive keys)
50
- config = RobotLab.config
51
-
52
- puts "Core Settings:"
53
- puts " max_iterations: #{config.max_iterations}"
54
- puts " streaming_enabled: #{config.streaming_enabled}"
55
- puts " template_path: #{config.template_path || '(default)'}"
56
- puts
57
-
58
- puts "RubyLLM Settings:"
59
- puts " model: #{config.ruby_llm.model}"
60
- puts " provider: #{config.ruby_llm.provider}"
61
- puts " request_timeout: #{config.ruby_llm.request_timeout}s"
62
- puts " max_retries: #{config.ruby_llm.max_retries}"
63
- puts " log_level: #{config.ruby_llm.log_level}"
64
-
65
- # Show where the LLM traffic actually goes
66
- puts " lms_api_base: #{LMS_API_BASE}"
67
- puts " (local inference — no API key in play)"
68
- puts
69
-
70
- section "Configuration Hierarchy (highest priority first)"
71
- puts
72
- puts " Per-Robot (override global settings for a specific robot):"
73
- puts " 11. Run-time context: kwargs to robot.run re-render template"
74
- puts " 10. with_* methods: robot.with_temperature(0.9).ask(...)"
75
- puts " 9. Constructor params: Robot.new(model: ..., temperature: ...)"
76
- puts " 8. Template front matter: model, temperature, etc. in .md YAML header"
77
- puts " 7. Robot RunConfig: config: passed to Robot constructor"
78
- puts
79
- puts " Network/Task (shared defaults for a group of robots):"
80
- puts " 6. Task RunConfig: config: passed to task() in network"
81
- puts " 5. Network RunConfig: config: passed to create_network()"
82
- puts
83
- puts " Global (apply to all robots unless overridden):"
84
- puts " 4. Environment variables: ROBOT_LAB_RUBY_LLM__MODEL, etc."
85
- puts " 3. Project config: ./config/robot_lab.yml"
86
- puts " 2. User config: ~/.config/robot_lab/config.yml"
87
- puts " 1. Bundled defaults: lib/robot_lab/config/defaults.yml + env overrides"
88
-
89
- section "RunConfig: Shared Operational Defaults"
90
-
91
- shared = RobotLab::RunConfig.new(model: LLM[:default].model, temperature: 0.5)
92
- puts " shared = RunConfig.new(model: #{LLM[:default].model.inspect}, temperature: 0.5)"
93
- puts " shared.to_h => #{shared.to_h.inspect}"
94
- puts
95
- puts " Note: RunConfig carries no `provider` field, so an LM Studio model still"
96
- puts " needs provider: passed to each robot alongside the shared config."
97
- puts
98
-
99
- creative = RobotLab::RunConfig.new(temperature: 0.9)
100
- merged = shared.merge(creative)
101
- puts " creative = RunConfig.new(temperature: 0.9)"
102
- puts " merged = shared.merge(creative)"
103
- puts " merged.to_h => #{merged.to_h.inspect}"
104
- puts " (model inherited from shared, temperature overridden by creative)"
105
- puts
106
-
107
- # Create a robot using the configuration
108
- robot = RobotLab.build(
109
- **llm_opts,
110
- name: "config_demo",
111
- template: :llm_config_demo,
112
- local_tools: [RobotLab::AskUser],
113
- # These are TEMPLATE parameters, not robot kwargs — the template renders
114
- # them into its prompt text.
115
- context: {
116
- environment: env,
117
- model: LLM[:default].model,
118
- provider: LLM[:default].provider
119
- }
120
- )
121
-
122
- puts "Running robot with #{env} configuration..."
123
- puts "Model: #{robot.model}"
124
- puts
125
-
126
- # Run the robot
127
- result = robot.run(
128
- "Briefly explain how environment-specific LLM configuration works " \
129
- "and why it's useful."
130
- )
131
-
132
- # Display the result
133
- puts "Response:"
134
- result.output.each do |message|
135
- puts message.content if message.respond_to?(:content)
136
- end
137
-
138
- puts <<~FOOTER
139
-
140
- #{"=" * 70}
141
- Configuration demonstrated successfully!
142
-
143
- Configuration flows through three layers:
144
- Global (MywayConfig): YAML defaults, env overrides, XDG, env vars
145
- Network/Task (RunConfig): shared defaults for groups of robots
146
- Per-Robot: RunConfig, template front matter, constructor params, with_*
147
-
148
- RunConfig lets you express shared defaults that flow through the hierarchy:
149
- shared = RobotLab::RunConfig.new(model: #{LLM[:default].model.inspect}, temperature: 0.5)
150
- network = RobotLab.create_network(name: "team", config: shared) { ... }
151
- robot = RobotLab.build(name: "bot", config: shared, temperature: 0.9)
152
-
153
- Example environment variable overrides:
154
- ROBOT_LAB_RUBY_LLM__MODEL=#{LLM[:default].model}
155
- ROBOT_LAB_RUBY_LLM__REQUEST_TIMEOUT=180
156
- LMS_API_BASE=http://localhost:1234/v1
157
-
158
- Try running with different environments:
159
- ROBOT_LAB_ENV=test ruby examples/08_llm_config.rb
160
- ROBOT_LAB_ENV=production ruby examples/08_llm_config.rb
161
- #{"=" * 70}
162
- FOOTER