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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +1 -1
- data/lib/robot_lab/mcp/transports/streamable_http.rb +1 -1
- data/lib/robot_lab/version.rb +1 -1
- metadata +3 -253
- data/.architecture/AGENTS.md +0 -32
- data/.architecture/config.yml +0 -8
- data/.architecture/members.yml +0 -60
- data/.architecture/reviews/feature-free-will.md +0 -490
- data/.architecture/reviews/overall-codebase.md +0 -427
- data/.claude/settings.local.json +0 -57
- data/.codex/config.toml +0 -2
- data/.envrc +0 -6
- data/.github/workflows/deploy-github-pages.yml +0 -52
- data/.irbrc +0 -6
- data/.loki +0 -65
- data/.quality/reek_baseline.txt +0 -43
- data/.rubocop.yml +0 -10
- data/Archspec.rb +0 -44
- data/CLAUDE.md +0 -141
- data/COMMITS.md +0 -196
- data/Rakefile +0 -193
- data/_typos.toml +0 -21
- data/agent2agent_review.md +0 -192
- data/agentf_improvements.md +0 -253
- data/agents.md +0 -14
- data/docs/api/core/index.md +0 -147
- data/docs/api/core/memory.md +0 -578
- data/docs/api/core/network.md +0 -553
- data/docs/api/core/result.md +0 -211
- data/docs/api/core/robot.md +0 -1667
- data/docs/api/core/state.md +0 -145
- data/docs/api/core/tool.md +0 -520
- data/docs/api/errors.md +0 -323
- data/docs/api/hooks.md +0 -469
- data/docs/api/index.md +0 -137
- data/docs/api/mcp/client.md +0 -387
- data/docs/api/mcp/index.md +0 -267
- data/docs/api/mcp/server.md +0 -252
- data/docs/api/mcp/transports.md +0 -339
- data/docs/api/messages/index.md +0 -126
- data/docs/api/messages/text-message.md +0 -170
- data/docs/api/messages/tool-call-message.md +0 -206
- data/docs/api/messages/tool-result-message.md +0 -242
- data/docs/api/messages/user-message.md +0 -232
- data/docs/api/skills.md +0 -423
- data/docs/api/streaming/context.md +0 -280
- data/docs/api/streaming/events.md +0 -185
- data/docs/api/streaming/index.md +0 -156
- data/docs/api/support.md +0 -846
- data/docs/architecture/core-concepts.md +0 -575
- data/docs/architecture/index.md +0 -252
- data/docs/architecture/message-flow.md +0 -366
- data/docs/architecture/network-orchestration.md +0 -478
- data/docs/architecture/robot-execution.md +0 -396
- data/docs/architecture/state-management.md +0 -410
- data/docs/assets/css/custom.css +0 -56
- data/docs/assets/images/robot_lab.jpg +0 -0
- data/docs/concepts.md +0 -641
- data/docs/examples/basic-chat.md +0 -258
- data/docs/examples/index.md +0 -425
- data/docs/examples/mcp-server.md +0 -403
- data/docs/examples/multi-robot-network.md +0 -476
- data/docs/examples/tool-usage.md +0 -468
- data/docs/getting-started/configuration.md +0 -645
- data/docs/getting-started/index.md +0 -56
- data/docs/getting-started/installation.md +0 -197
- data/docs/getting-started/quick-start.md +0 -293
- data/docs/guides/building-robots.md +0 -1187
- data/docs/guides/creating-networks.md +0 -736
- data/docs/guides/hooks.md +0 -1206
- data/docs/guides/index.md +0 -78
- data/docs/guides/knowledge.md +0 -219
- data/docs/guides/mcp-integration.md +0 -740
- data/docs/guides/memory.md +0 -458
- data/docs/guides/observability.md +0 -733
- data/docs/guides/streaming.md +0 -323
- data/docs/guides/using-tools.md +0 -780
- data/docs/index.md +0 -280
- data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
- data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
- data/examples/.envrc +0 -3
- data/examples/01_simple_robot.rb +0 -31
- data/examples/02_tools.rb +0 -109
- data/examples/03_network.rb +0 -118
- data/examples/04_mcp.rb +0 -217
- data/examples/05_streaming.rb +0 -137
- data/examples/06_prompt_templates.rb +0 -325
- data/examples/07_network_memory.rb +0 -313
- data/examples/08_llm_config.rb +0 -162
- data/examples/09_chaining.rb +0 -251
- data/examples/10_memory.rb +0 -315
- data/examples/11_network_introspection.rb +0 -239
- data/examples/12_message_bus.rb +0 -67
- data/examples/13_spawn.rb +0 -85
- data/examples/14_rusty_circuit/.envrc +0 -1
- data/examples/14_rusty_circuit/.gitignore +0 -1
- data/examples/14_rusty_circuit/README.md +0 -1
- data/examples/14_rusty_circuit/comic.rb +0 -149
- data/examples/14_rusty_circuit/display.rb +0 -217
- data/examples/14_rusty_circuit/heckler.rb +0 -65
- data/examples/14_rusty_circuit/open_mic.rb +0 -132
- data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
- data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
- data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
- data/examples/14_rusty_circuit/scout.rb +0 -165
- data/examples/14_rusty_circuit/show.log +0 -234
- data/examples/15_memory_network_and_bus/.envrc +0 -1
- data/examples/15_memory_network_and_bus/README.md +0 -66
- data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
- data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
- data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
- data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
- data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
- data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
- data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
- data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
- data/examples/15_memory_network_and_bus/output/memory.json +0 -13
- data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
- data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
- data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
- data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
- data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
- data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
- data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
- data/examples/16_writers_room/.envrc +0 -1
- data/examples/16_writers_room/display.rb +0 -158
- data/examples/16_writers_room/output/.gitignore +0 -4
- data/examples/16_writers_room/output/README.md +0 -69
- data/examples/16_writers_room/output/opus_001.md +0 -263
- data/examples/16_writers_room/output/opus_001_notes.log +0 -470
- data/examples/16_writers_room/output/opus_002.md +0 -245
- data/examples/16_writers_room/output/opus_002_notes.log +0 -546
- data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
- data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
- data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
- data/examples/16_writers_room/prompts/writer.md +0 -37
- data/examples/16_writers_room/room.rb +0 -186
- data/examples/16_writers_room/tools.rb +0 -173
- data/examples/16_writers_room/writer.rb +0 -121
- data/examples/16_writers_room/writers_room.rb +0 -256
- data/examples/17_skills.rb +0 -234
- data/examples/18_rails/.envrc +0 -3
- data/examples/18_rails/.gitignore +0 -5
- data/examples/18_rails/Gemfile +0 -11
- data/examples/18_rails/README.md +0 -67
- data/examples/18_rails/Rakefile +0 -4
- data/examples/18_rails/app/controllers/application_controller.rb +0 -4
- data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
- data/examples/18_rails/app/jobs/application_job.rb +0 -4
- data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
- data/examples/18_rails/app/models/application_record.rb +0 -5
- data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
- data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
- data/examples/18_rails/app/robots/chat_robot.rb +0 -25
- data/examples/18_rails/app/tools/time_tool.rb +0 -9
- data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
- data/examples/18_rails/app/views/chat/index.html.erb +0 -67
- data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
- data/examples/18_rails/bin/dev +0 -7
- data/examples/18_rails/bin/rails +0 -6
- data/examples/18_rails/bin/setup +0 -15
- data/examples/18_rails/config/application.rb +0 -33
- data/examples/18_rails/config/cable.yml +0 -2
- data/examples/18_rails/config/database.yml +0 -5
- data/examples/18_rails/config/environment.rb +0 -4
- data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
- data/examples/18_rails/config/routes.rb +0 -6
- data/examples/18_rails/config.ru +0 -4
- data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
- data/examples/19_token_tracking.rb +0 -138
- data/examples/20_circuit_breaker.rb +0 -147
- data/examples/21_learning_loop.rb +0 -181
- data/examples/22_context_compression.rb +0 -172
- data/examples/23_convergence.rb +0 -133
- data/examples/24_structured_delegation.rb +0 -151
- data/examples/25_history_search/conversation.jsonl +0 -30
- data/examples/25_history_search.rb +0 -133
- data/examples/26_document_store/api_versioning_adr.md +0 -52
- data/examples/26_document_store/incident_postmortem.md +0 -46
- data/examples/26_document_store/postgres_runbook.md +0 -49
- data/examples/26_document_store/redis_caching_guide.md +0 -48
- data/examples/26_document_store/sidekiq_guide.md +0 -51
- data/examples/26_document_store.rb +0 -140
- data/examples/27_incident_response/README.md +0 -65
- data/examples/27_incident_response/incident_response.rb +0 -261
- data/examples/28_mcp_discovery.rb +0 -113
- data/examples/29_ractor_tools.rb +0 -240
- data/examples/30_ractor_network.rb +0 -252
- data/examples/31_launch_assessment.rb +0 -246
- data/examples/32_newsletter_reader.rb +0 -193
- data/examples/33_stock_generator.rb +0 -80
- data/examples/33_stock_predictor.rb +0 -327
- data/examples/34_agentskills.rb +0 -76
- data/examples/35_hooks.rb +0 -266
- data/examples/README.md +0 -550
- data/examples/common.rb +0 -180
- data/examples/prompts/assistant.md +0 -5
- data/examples/prompts/audit_trail.md +0 -8
- data/examples/prompts/billing.md +0 -10
- data/examples/prompts/classifier.md +0 -7
- data/examples/prompts/comedian.md +0 -6
- data/examples/prompts/comedy_critic.md +0 -10
- data/examples/prompts/configurable.md +0 -9
- data/examples/prompts/dispatcher.md +0 -12
- data/examples/prompts/entity_extractor.md +0 -14
- data/examples/prompts/escalation.md +0 -42
- data/examples/prompts/frontmatter_mcp_test.md +0 -9
- data/examples/prompts/frontmatter_named_test.md +0 -5
- data/examples/prompts/frontmatter_tools_test.md +0 -6
- data/examples/prompts/general.md +0 -7
- data/examples/prompts/github_assistant.md +0 -14
- data/examples/prompts/helper.md +0 -4
- data/examples/prompts/incident_responder.md +0 -18
- data/examples/prompts/keyword_extractor.md +0 -11
- data/examples/prompts/llm_config_demo.md +0 -20
- data/examples/prompts/order_support.md +0 -35
- data/examples/prompts/os_advocate.md +0 -13
- data/examples/prompts/os_chief.md +0 -13
- data/examples/prompts/os_editor.md +0 -13
- data/examples/prompts/parameterized_main_test.md +0 -6
- data/examples/prompts/pii_redactor.md +0 -7
- data/examples/prompts/product_support.md +0 -37
- data/examples/prompts/runbook_protocol.md +0 -12
- data/examples/prompts/sentiment_analyzer.md +0 -12
- data/examples/prompts/skill_a_test.md +0 -4
- data/examples/prompts/skill_b_test.md +0 -4
- data/examples/prompts/skill_config_test.md +0 -5
- data/examples/prompts/skill_cycle_a_test.md +0 -6
- data/examples/prompts/skill_cycle_b_test.md +0 -6
- data/examples/prompts/skill_description_test.md +0 -4
- data/examples/prompts/skill_leaf_test.md +0 -4
- data/examples/prompts/skill_nested_test.md +0 -6
- data/examples/prompts/skill_refs_main_test.md +0 -6
- data/examples/prompts/skill_self_ref_test.md +0 -6
- data/examples/prompts/skill_with_mcp_test.md +0 -9
- data/examples/prompts/skill_with_params_test.md +0 -6
- data/examples/prompts/skill_with_robot_name_test.md +0 -5
- data/examples/prompts/skill_with_tools_test.md +0 -6
- data/examples/prompts/sre_compliance.md +0 -10
- data/examples/prompts/structured_output.md +0 -7
- data/examples/prompts/synthesizer.md +0 -17
- data/examples/prompts/technical.md +0 -10
- data/examples/prompts/template_with_skills_test.md +0 -6
- data/examples/prompts/triage.md +0 -22
- data/examples/ruboruby.md +0 -423
- data/examples/run_all.rb +0 -60
- data/examples/xyzzy.rb +0 -97
- data/logfile +0 -8
- data/mkdocs.yml +0 -209
- data/robot_concurrency.md +0 -38
- data/simple_acp_review.md +0 -298
- data/temp.md +0 -6
- 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!"
|
data/examples/05_streaming.rb
DELETED
|
@@ -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
|