robot_lab 0.0.1 → 0.0.4
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/.github/workflows/deploy-github-pages.yml +9 -9
- data/.irbrc +6 -0
- data/CHANGELOG.md +90 -0
- data/README.md +203 -46
- data/Rakefile +70 -1
- data/docs/api/core/index.md +12 -0
- data/docs/api/core/robot.md +478 -130
- data/docs/api/core/tool.md +205 -209
- data/docs/api/history/active-record-adapter.md +174 -94
- data/docs/api/history/config.md +186 -93
- data/docs/api/history/index.md +57 -61
- data/docs/api/history/thread-manager.md +123 -73
- data/docs/api/mcp/client.md +119 -48
- data/docs/api/mcp/index.md +75 -60
- data/docs/api/mcp/server.md +120 -136
- data/docs/api/mcp/transports.md +172 -184
- data/docs/api/streaming/context.md +157 -74
- data/docs/api/streaming/events.md +114 -166
- data/docs/api/streaming/index.md +74 -72
- data/docs/architecture/core-concepts.md +361 -112
- data/docs/architecture/index.md +97 -59
- data/docs/architecture/message-flow.md +138 -129
- data/docs/architecture/network-orchestration.md +197 -50
- data/docs/architecture/robot-execution.md +199 -146
- data/docs/architecture/state-management.md +255 -187
- data/docs/concepts.md +312 -48
- data/docs/examples/basic-chat.md +89 -77
- data/docs/examples/index.md +222 -47
- data/docs/examples/mcp-server.md +207 -203
- data/docs/examples/multi-robot-network.md +129 -35
- data/docs/examples/rails-application.md +159 -160
- data/docs/examples/tool-usage.md +295 -204
- data/docs/getting-started/configuration.md +275 -162
- data/docs/getting-started/index.md +1 -1
- data/docs/getting-started/installation.md +22 -13
- data/docs/getting-started/quick-start.md +166 -121
- data/docs/guides/building-robots.md +417 -212
- data/docs/guides/creating-networks.md +94 -24
- data/docs/guides/mcp-integration.md +152 -113
- data/docs/guides/memory.md +220 -164
- data/docs/guides/streaming.md +80 -110
- data/docs/guides/using-tools.md +259 -212
- data/docs/index.md +50 -37
- data/examples/01_simple_robot.rb +6 -9
- data/examples/02_tools.rb +6 -9
- data/examples/03_network.rb +13 -14
- data/examples/04_mcp.rb +5 -8
- data/examples/05_streaming.rb +5 -8
- data/examples/06_prompt_templates.rb +42 -37
- data/examples/07_network_memory.rb +13 -14
- data/examples/08_llm_config.rb +140 -0
- data/examples/09_chaining.rb +223 -0
- data/examples/10_memory.rb +331 -0
- data/examples/11_network_introspection.rb +230 -0
- data/examples/12_message_bus.rb +74 -0
- data/examples/13_spawn.rb +90 -0
- data/examples/14_rusty_circuit/comic.rb +143 -0
- data/examples/14_rusty_circuit/display.rb +203 -0
- data/examples/14_rusty_circuit/heckler.rb +57 -0
- data/examples/14_rusty_circuit/open_mic.rb +121 -0
- data/examples/14_rusty_circuit/prompts/open_mic_comic.md +20 -0
- data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +23 -0
- data/examples/14_rusty_circuit/prompts/open_mic_scout.md +20 -0
- data/examples/14_rusty_circuit/scout.rb +173 -0
- data/examples/14_rusty_circuit/scout_notes.md +89 -0
- data/examples/14_rusty_circuit/show.log +234 -0
- data/examples/15_memory_network_and_bus/editor_in_chief.rb +24 -0
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +206 -0
- data/examples/15_memory_network_and_bus/linux_writer.rb +80 -0
- data/examples/15_memory_network_and_bus/os_editor.rb +46 -0
- data/examples/15_memory_network_and_bus/os_writer.rb +46 -0
- data/examples/15_memory_network_and_bus/output/combined_article.md +13 -0
- data/examples/15_memory_network_and_bus/output/final_article.md +15 -0
- data/examples/15_memory_network_and_bus/output/linux_draft.md +5 -0
- data/examples/15_memory_network_and_bus/output/mac_draft.md +7 -0
- data/examples/15_memory_network_and_bus/output/memory.json +13 -0
- data/examples/15_memory_network_and_bus/output/revision_1.md +19 -0
- data/examples/15_memory_network_and_bus/output/revision_2.md +15 -0
- data/examples/15_memory_network_and_bus/output/windows_draft.md +7 -0
- data/examples/15_memory_network_and_bus/prompts/os_advocate.md +13 -0
- data/examples/15_memory_network_and_bus/prompts/os_chief.md +13 -0
- data/examples/15_memory_network_and_bus/prompts/os_editor.md +13 -0
- data/examples/README.md +197 -0
- data/examples/prompts/{assistant/system.txt.erb → assistant.md} +3 -0
- data/examples/prompts/{billing/system.txt.erb → billing.md} +3 -0
- data/examples/prompts/{classifier/system.txt.erb → classifier.md} +3 -0
- data/examples/prompts/comedian.md +6 -0
- data/examples/prompts/comedy_critic.md +10 -0
- data/examples/prompts/configurable.md +9 -0
- data/examples/prompts/dispatcher.md +12 -0
- data/examples/prompts/{entity_extractor/system.txt.erb → entity_extractor.md} +3 -0
- data/examples/prompts/{escalation/system.txt.erb → escalation.md} +7 -0
- data/examples/prompts/frontmatter_mcp_test.md +9 -0
- data/examples/prompts/frontmatter_named_test.md +5 -0
- data/examples/prompts/frontmatter_tools_test.md +6 -0
- data/examples/prompts/{general/system.txt.erb → general.md} +3 -0
- data/examples/prompts/{github_assistant/system.txt.erb → github_assistant.md} +8 -0
- data/examples/prompts/{helper/system.txt.erb → helper.md} +3 -0
- data/examples/prompts/{keyword_extractor/system.txt.erb → keyword_extractor.md} +3 -0
- data/examples/prompts/llm_config_demo.md +20 -0
- data/examples/prompts/{order_support/system.txt.erb → order_support.md} +8 -0
- data/examples/prompts/os_advocate.md +13 -0
- data/examples/prompts/os_chief.md +13 -0
- data/examples/prompts/os_editor.md +13 -0
- data/examples/prompts/{product_support/system.txt.erb → product_support.md} +7 -0
- data/examples/prompts/{sentiment_analyzer/system.txt.erb → sentiment_analyzer.md} +3 -0
- data/examples/prompts/{synthesizer/system.txt.erb → synthesizer.md} +3 -0
- data/examples/prompts/{technical/system.txt.erb → technical.md} +3 -0
- data/examples/prompts/{triage/system.txt.erb → triage.md} +6 -0
- data/lib/generators/robot_lab/templates/initializer.rb.tt +1 -1
- data/lib/robot_lab/adapters/openai.rb +2 -1
- data/lib/robot_lab/ask_user.rb +75 -0
- data/lib/robot_lab/config/defaults.yml +121 -0
- data/lib/robot_lab/config.rb +183 -0
- data/lib/robot_lab/error.rb +6 -0
- data/lib/robot_lab/mcp/client.rb +1 -1
- data/lib/robot_lab/memory.rb +2 -2
- data/lib/robot_lab/robot.rb +523 -249
- data/lib/robot_lab/robot_message.rb +44 -0
- data/lib/robot_lab/robot_result.rb +1 -0
- data/lib/robot_lab/robotic_model.rb +1 -1
- data/lib/robot_lab/streaming/context.rb +1 -1
- data/lib/robot_lab/tool.rb +108 -172
- data/lib/robot_lab/tool_config.rb +1 -1
- data/lib/robot_lab/tool_manifest.rb +2 -18
- data/lib/robot_lab/version.rb +1 -1
- data/lib/robot_lab.rb +66 -55
- metadata +107 -116
- data/examples/prompts/assistant/user.txt.erb +0 -1
- data/examples/prompts/billing/user.txt.erb +0 -1
- data/examples/prompts/classifier/user.txt.erb +0 -1
- data/examples/prompts/entity_extractor/user.txt.erb +0 -3
- data/examples/prompts/escalation/user.txt.erb +0 -34
- data/examples/prompts/general/user.txt.erb +0 -1
- data/examples/prompts/github_assistant/user.txt.erb +0 -1
- data/examples/prompts/helper/user.txt.erb +0 -1
- data/examples/prompts/keyword_extractor/user.txt.erb +0 -3
- data/examples/prompts/order_support/user.txt.erb +0 -22
- data/examples/prompts/product_support/user.txt.erb +0 -32
- data/examples/prompts/sentiment_analyzer/user.txt.erb +0 -3
- data/examples/prompts/synthesizer/user.txt.erb +0 -15
- data/examples/prompts/technical/user.txt.erb +0 -1
- data/examples/prompts/triage/user.txt.erb +0 -17
- data/lib/robot_lab/configuration.rb +0 -143
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8d3bfa06c07301b03b01359a15a945374b135f2055c77396ad456a916917a742
|
|
4
|
+
data.tar.gz: 0b9e81d50841bf9a56d4efcc8f4175f34aa0e17702c89cc4bd5d4750631c0f68
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 15649fd320dd84530d49884900d99c2cdc98ddd5841948b052ed6b3a6f3d0fa9cec3e80636883f6d2bcd5b5a8ffe6b61ad681dc907faf5e881f48ac05ebef0fd
|
|
7
|
+
data.tar.gz: cdd136aad382247babb5639cf3a2b0b35db5086b33661823a6e00d8099f2a18a57325c3ed57f3474d23ee8f317f65c1f0b70911d349ad04f111d80cac03dd8c9
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
name: Deploy
|
|
1
|
+
name: Deploy Documentation to GitHub Pages
|
|
2
2
|
on:
|
|
3
3
|
push:
|
|
4
4
|
branches:
|
|
@@ -41,12 +41,12 @@ jobs:
|
|
|
41
41
|
git config --local user.email "action@github.com"
|
|
42
42
|
git config --local user.name "GitHub Action"
|
|
43
43
|
|
|
44
|
+
- name: Build MkDocs site
|
|
45
|
+
run: mkdocs build
|
|
46
|
+
|
|
44
47
|
- name: Deploy to GitHub Pages
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
echo "Deploying from develop branch"
|
|
51
|
-
mkdocs gh-deploy --force --clean
|
|
52
|
-
fi
|
|
48
|
+
uses: peaceiris/actions-gh-pages@v4
|
|
49
|
+
with:
|
|
50
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
51
|
+
publish_dir: ./site
|
|
52
|
+
keep_files: true
|
data/.irbrc
ADDED
data/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,96 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [0.0.4] - 2026-02-16 [unreleased]
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`AskUser` tool** for human-in-the-loop interactions
|
|
19
|
+
- Supports open-ended text, multiple choice, and default values
|
|
20
|
+
- IO sourced from `robot.input`/`robot.output` (defaults to `$stdin`/`$stdout`)
|
|
21
|
+
- Full test suite (`test/robot_lab/ask_user_test.rb`)
|
|
22
|
+
- **`Robot#input` / `Robot#output` accessors** for configurable IO streams
|
|
23
|
+
- **`reply` alias** for `RobotResult#last_text_content` — shorter, more natural API
|
|
24
|
+
- **`.irbrc`** for loading RobotLab in project-level IRB sessions
|
|
25
|
+
- **`wait_until` test helper** replacing flaky `sleep`-based assertions in async tests
|
|
26
|
+
- Documentation for AskUser tool across API reference, guides, and examples
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Bumped version to 0.0.4
|
|
31
|
+
- **Made Rails dependencies optional** — removed `railties`, `activerecord`, `state_machines`, `state_machines-activemodel`, `state_machines-activerecord` from gemspec hard dependencies; moved to Gemfile `:test` group
|
|
32
|
+
- Replaced `require 'active_support'` with targeted `require 'active_support/core_ext/module/delegation'` — only loads what ruby_llm actually needs
|
|
33
|
+
- Added `activesupport >= 7.0` as explicit gemspec dependency with comment explaining it's required by ruby_llm (undeclared upstream)
|
|
34
|
+
- **Tool JSON schema keys are now symbolized** via `deep_symbolize_keys` in `Tool#to_json_schema`
|
|
35
|
+
- Updated all examples to use `reply` alias instead of `last_text_content`
|
|
36
|
+
- Replaced `sleep`-based test assertions with `wait_until` helper in memory, waiter, and robot tests
|
|
37
|
+
- Disabled branch coverage in SimpleCov except in CI
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- Gem install conflict (`activesupport` version mismatch) when running outside Bundler
|
|
42
|
+
- IRB loading issue where `require_relative` was a no-op due to partial load in `$LOADED_FEATURES` — switched to `load`
|
|
43
|
+
- Robot tests for `send_message` now register a message handler on the receiver to avoid TypedBus warnings
|
|
44
|
+
|
|
45
|
+
## [0.0.3] - 2026-02-15 [unreleased]
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- **Self-contained templates** with extended YAML front matter support
|
|
50
|
+
- `robot_name` — override robot name from template
|
|
51
|
+
- `description` — set robot description from template
|
|
52
|
+
- `tools` — declare tool class names (resolved via `Object.const_get` at build time)
|
|
53
|
+
- `mcp` — declare MCP server configurations
|
|
54
|
+
- Constructor-provided values always take precedence over front matter
|
|
55
|
+
- **Editorial pipeline example** (`15_memory_network_and_bus/`) demonstrating multi-stage workflow with network, memory, and bus coordination
|
|
56
|
+
- OS-specific writer robots, editor, and editor-in-chief roles
|
|
57
|
+
- New prompt templates: `os_advocate`, `os_editor`, `os_chief`
|
|
58
|
+
- Rakefile support for running subdirectory-based examples with `SUBDIR_ENTRY_POINTS` mapping
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
|
|
62
|
+
- Bumped version to 0.0.3
|
|
63
|
+
- Refactored Comic and Scout classes to use `attr_accessor` instead of `instance_variable_set`/`instance_variable_get`
|
|
64
|
+
- Extensive documentation updates across README, guides, API reference, and examples for front matter extras
|
|
65
|
+
|
|
66
|
+
## [0.0.2] - 2026-02-15 (unreleased)
|
|
67
|
+
|
|
68
|
+
### Added
|
|
69
|
+
|
|
70
|
+
- **TypedBus message bus** for robot-to-robot communication
|
|
71
|
+
- `RobotMessage` immutable data class (`Data.define`) with `id`, `from`, `content`, `in_reply_to`
|
|
72
|
+
- Optional `bus:` parameter on Robot constructor — purely additive
|
|
73
|
+
- `on_message` handler with auto-ACK (1-arg block) and manual ACK/NACK (2-arg block)
|
|
74
|
+
- `publish_to_bus` with Async-aware fiber wrapping
|
|
75
|
+
- Typed channels accepting only `RobotMessage` objects
|
|
76
|
+
- **Dynamic robot spawning** via `Robot#spawn` method for creating child robots at runtime
|
|
77
|
+
- **`with_bus` configuration method** for connecting robots to a message bus after creation
|
|
78
|
+
- **Comic robot class** with dynamic comedy tools (`reinvent_style`, `adjust_energy`, `get_coaching`)
|
|
79
|
+
- New examples:
|
|
80
|
+
- `12_message_bus.rb` — two-robot joke critique workflow
|
|
81
|
+
- `13_spawn.rb` — dynamic robot spawning
|
|
82
|
+
- `14_rusty_circuit/` — multi-robot comedy open mic with bus-based coordination
|
|
83
|
+
- New prompt templates: `comedian`, `comedy_critic`, `dispatcher`, `open_mic_comic`, `open_mic_heckler`, `open_mic_scout`, `configurable`, `llm_config_demo`
|
|
84
|
+
- Rake tasks for building documentation sites
|
|
85
|
+
- GitHub Actions workflow for YARD documentation deployment
|
|
86
|
+
|
|
87
|
+
### Changed
|
|
88
|
+
|
|
89
|
+
- Bumped version to 0.0.2
|
|
90
|
+
- Replaced `ruby_llm-template` dependency with `prompt_manager` (~> 1.0)
|
|
91
|
+
- Updated `ruby_llm` dependency to ~> 1.12
|
|
92
|
+
- Added `typed_bus` as a core dependency
|
|
93
|
+
- Added `myway_config` (~> 0.1) dependency
|
|
94
|
+
- Added `amazing_print` and `hashdiff` as development dependencies
|
|
95
|
+
- Migrated all prompt templates from directory-based format (`system.txt.erb` / `user.txt.erb`) to single `.md` files with YAML front matter
|
|
96
|
+
- Refactored `Robot` class for simplified configuration
|
|
97
|
+
- Refactored `Config` class
|
|
98
|
+
- Extensive documentation updates across all guide, architecture, and API reference pages
|
|
99
|
+
|
|
100
|
+
### Fixed
|
|
101
|
+
|
|
102
|
+
- GitHub Actions platform limitation (`arm64-darwin` only in lockfile)
|
|
103
|
+
|
|
14
104
|
## [0.0.1] - 2026-01-16
|
|
15
105
|
|
|
16
106
|
- refactored the network concept
|
data/README.md
CHANGED
|
@@ -18,8 +18,10 @@ RobotLab enables you to build sophisticated AI applications using multiple speci
|
|
|
18
18
|
- <strong>Network Orchestration</strong> - Connect robots with flexible routing<br>
|
|
19
19
|
- <strong>Extensible Tools</strong> - Give robots custom capabilities<br>
|
|
20
20
|
- <strong>MCP Integration</strong> - Connect to external tool servers<br>
|
|
21
|
-
- <strong>Shared Memory</strong> -
|
|
21
|
+
- <strong>Shared Memory</strong> - Reactive key-value store with subscriptions<br>
|
|
22
22
|
- <strong>Conversation History</strong> - Persist and restore threads<br>
|
|
23
|
+
- <strong>Message Bus</strong> - Bidirectional robot communication via TypedBus<br>
|
|
24
|
+
- <strong>Dynamic Spawning</strong> - Robots create new robots at runtime<br>
|
|
23
25
|
- <strong>Streaming</strong> - Real-time event streaming<br>
|
|
24
26
|
- <strong>Rails Integration</strong> - Generators and ActiveRecord support
|
|
25
27
|
</td>
|
|
@@ -52,11 +54,6 @@ The simplest way to create a robot is with an inline `system_prompt`. This appro
|
|
|
52
54
|
```ruby
|
|
53
55
|
require "robot_lab"
|
|
54
56
|
|
|
55
|
-
# Configure RobotLab
|
|
56
|
-
RobotLab.configure do |config|
|
|
57
|
-
config.default_model = "claude-sonnet-4"
|
|
58
|
-
end
|
|
59
|
-
|
|
60
57
|
# Create a robot with an inline system prompt
|
|
61
58
|
robot = RobotLab.build(
|
|
62
59
|
name: "assistant",
|
|
@@ -64,66 +61,119 @@ robot = RobotLab.build(
|
|
|
64
61
|
)
|
|
65
62
|
|
|
66
63
|
# Run the robot
|
|
67
|
-
result = robot.run(
|
|
64
|
+
result = robot.run("What is the capital of France?")
|
|
68
65
|
|
|
69
|
-
puts result.
|
|
66
|
+
puts result.last_text_content
|
|
70
67
|
# => "The capital of France is Paris."
|
|
71
68
|
```
|
|
72
69
|
|
|
73
|
-
###
|
|
70
|
+
### Configuration
|
|
74
71
|
|
|
75
|
-
|
|
72
|
+
RobotLab uses [MywayConfig](https://github.com/MadBomber/myway_config) for layered configuration. There is no `configure` block. Configuration is loaded automatically from multiple sources in priority order:
|
|
76
73
|
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
1. Bundled defaults (`lib/robot_lab/config/defaults.yml`)
|
|
75
|
+
2. Environment-specific overrides (development, test, production)
|
|
76
|
+
3. XDG user config (`~/.config/robot_lab/config.yml`)
|
|
77
|
+
4. Project config (`./config/robot_lab.yml`)
|
|
78
|
+
5. Environment variables (`ROBOT_LAB_*` prefix)
|
|
81
79
|
|
|
82
|
-
|
|
80
|
+
```bash
|
|
81
|
+
# Set API keys via environment variables (double underscore for nesting)
|
|
82
|
+
export ROBOT_LAB_RUBY_LLM__ANTHROPIC_API_KEY=sk-ant-...
|
|
83
|
+
export ROBOT_LAB_RUBY_LLM__OPENAI_API_KEY=sk-...
|
|
84
|
+
export ROBOT_LAB_RUBY_LLM__MODEL=claude-sonnet-4
|
|
85
|
+
```
|
|
83
86
|
|
|
84
87
|
```ruby
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
+
# Access configuration values
|
|
89
|
+
RobotLab.config.ruby_llm.model #=> "claude-sonnet-4"
|
|
90
|
+
RobotLab.config.ruby_llm.request_timeout #=> 120
|
|
91
|
+
RobotLab.config.streaming_enabled #=> true
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Or create a project config file at `./config/robot_lab.yml`:
|
|
95
|
+
|
|
96
|
+
```yaml
|
|
97
|
+
ruby_llm:
|
|
98
|
+
model: claude-sonnet-4
|
|
99
|
+
anthropic_api_key: sk-ant-...
|
|
100
|
+
request_timeout: 180
|
|
88
101
|
```
|
|
89
102
|
|
|
90
|
-
|
|
103
|
+
### Using Templates
|
|
104
|
+
|
|
105
|
+
For production applications, RobotLab supports a template system built on [PromptManager](https://github.com/MadBomber/prompt_manager). Templates allow you to:
|
|
106
|
+
|
|
107
|
+
- **Compose prompts** from reusable Markdown files
|
|
108
|
+
- **Inject dynamic context** at build-time
|
|
109
|
+
- **Version control** your prompts alongside your code
|
|
110
|
+
- **Share prompts** across multiple robots
|
|
111
|
+
|
|
112
|
+
Each template is a `.md` file with YAML front matter for metadata and parameters:
|
|
91
113
|
|
|
92
114
|
```
|
|
93
|
-
|
|
94
|
-
assistant
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
├── assistant.txt.erb # Pre-filled assistant response (optional)
|
|
98
|
-
└── schema.rb # Structured output schema (optional)
|
|
115
|
+
prompts/
|
|
116
|
+
assistant.md
|
|
117
|
+
classifier.md
|
|
118
|
+
billing.md
|
|
99
119
|
```
|
|
100
120
|
|
|
101
|
-
Create
|
|
121
|
+
Create a template at `prompts/assistant.md`:
|
|
102
122
|
|
|
103
|
-
```
|
|
123
|
+
```markdown
|
|
124
|
+
---
|
|
125
|
+
description: A helpful assistant
|
|
126
|
+
parameters:
|
|
127
|
+
company_name: null
|
|
128
|
+
tone: friendly
|
|
129
|
+
---
|
|
104
130
|
You are a helpful assistant for <%= company_name %>.
|
|
105
131
|
|
|
132
|
+
Your communication style should be <%= tone %>.
|
|
133
|
+
|
|
106
134
|
Your responsibilities:
|
|
107
135
|
- Answer questions accurately and concisely
|
|
108
136
|
- Be friendly and professional
|
|
109
137
|
- Admit when you don't know something
|
|
110
|
-
|
|
111
|
-
<% if guidelines %>
|
|
112
|
-
Additional guidelines:
|
|
113
|
-
<%= guidelines %>
|
|
114
|
-
<% end %>
|
|
115
138
|
```
|
|
116
139
|
|
|
117
|
-
Reference the template
|
|
140
|
+
Reference the template by symbol:
|
|
118
141
|
|
|
119
142
|
```ruby
|
|
120
143
|
robot = RobotLab.build(
|
|
121
144
|
name: "assistant",
|
|
122
145
|
template: :assistant,
|
|
123
|
-
context: { company_name: "Acme Corp",
|
|
146
|
+
context: { company_name: "Acme Corp", tone: "professional" }
|
|
124
147
|
)
|
|
125
148
|
```
|
|
126
149
|
|
|
150
|
+
### Self-Contained Templates
|
|
151
|
+
|
|
152
|
+
Templates can declare tools, MCP servers, name, and description in front matter, making the `.md` file a complete robot definition:
|
|
153
|
+
|
|
154
|
+
```markdown
|
|
155
|
+
---
|
|
156
|
+
description: GitHub assistant with MCP tool access
|
|
157
|
+
robot_name: github_bot
|
|
158
|
+
tools:
|
|
159
|
+
- CodeSearchTool
|
|
160
|
+
mcp:
|
|
161
|
+
- name: github
|
|
162
|
+
transport: stdio
|
|
163
|
+
command: npx
|
|
164
|
+
args: ["-y", "@modelcontextprotocol/server-github"]
|
|
165
|
+
model: claude-sonnet-4
|
|
166
|
+
---
|
|
167
|
+
You are a GitHub assistant. Use available tools to help with repository tasks.
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
# Template provides everything — minimal constructor call
|
|
172
|
+
robot = RobotLab.build(template: :github_assistant)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Front matter supports: `description`, `robot_name`, `tools`, `mcp`, `parameters`, and LLM config keys (`model`, `temperature`, `top_p`, `top_k`, `max_tokens`, `presence_penalty`, `frequency_penalty`, `stop`). Constructor-provided values always take precedence over front matter.
|
|
176
|
+
|
|
127
177
|
### Combining Templates with System Prompts
|
|
128
178
|
|
|
129
179
|
The `system_prompt` parameter can also be used alongside a template. When both are provided, the template renders first and the `system_prompt` is appended. This is particularly useful during development and testing when you want to add temporary instructions or context to an existing template:
|
|
@@ -132,11 +182,25 @@ The `system_prompt` parameter can also be used alongside a template. When both a
|
|
|
132
182
|
robot = RobotLab.build(
|
|
133
183
|
name: "assistant",
|
|
134
184
|
template: :assistant,
|
|
135
|
-
context: { company_name: "Acme Corp" },
|
|
185
|
+
context: { company_name: "Acme Corp", tone: "friendly" },
|
|
136
186
|
system_prompt: "DEBUG MODE: Log all tool calls. Today's date is #{Date.today}."
|
|
137
187
|
)
|
|
138
188
|
```
|
|
139
189
|
|
|
190
|
+
### Chaining Configuration
|
|
191
|
+
|
|
192
|
+
Robots support method chaining to adjust configuration after creation:
|
|
193
|
+
|
|
194
|
+
```ruby
|
|
195
|
+
robot = RobotLab.build(name: "writer", system_prompt: "You are a creative writer.")
|
|
196
|
+
|
|
197
|
+
result = robot
|
|
198
|
+
.with_temperature(0.9)
|
|
199
|
+
.with_model("claude-sonnet-4")
|
|
200
|
+
.with_max_tokens(2000)
|
|
201
|
+
.run("Write a haiku about Ruby programming")
|
|
202
|
+
```
|
|
203
|
+
|
|
140
204
|
## Creating a Robot with Tools
|
|
141
205
|
|
|
142
206
|
```ruby
|
|
@@ -163,14 +227,14 @@ class Magic8Ball < RubyLLM::Tool
|
|
|
163
227
|
end
|
|
164
228
|
end
|
|
165
229
|
|
|
166
|
-
# Create robot with tools
|
|
230
|
+
# Create robot with tools via local_tools: parameter
|
|
167
231
|
robot = RobotLab.build(
|
|
168
232
|
name: "oracle",
|
|
169
233
|
system_prompt: "You are a mystical oracle. Use the Magic 8-Ball to answer questions about the future.",
|
|
170
|
-
|
|
234
|
+
local_tools: [Magic8Ball]
|
|
171
235
|
)
|
|
172
236
|
|
|
173
|
-
result = robot.run(
|
|
237
|
+
result = robot.run("Should I start learning Rust?")
|
|
174
238
|
```
|
|
175
239
|
|
|
176
240
|
## Orchestrating Multiple Robots
|
|
@@ -181,7 +245,10 @@ Networks use [SimpleFlow](https://github.com/MadBomber/simple_flow) pipelines wi
|
|
|
181
245
|
# Custom classifier that activates the appropriate specialist
|
|
182
246
|
class ClassifierRobot < RobotLab::Robot
|
|
183
247
|
def call(result)
|
|
184
|
-
|
|
248
|
+
context = extract_run_context(result)
|
|
249
|
+
message = context.delete(:message)
|
|
250
|
+
|
|
251
|
+
robot_result = run(message, **context)
|
|
185
252
|
|
|
186
253
|
new_result = result
|
|
187
254
|
.with_context(@name.to_sym, robot_result)
|
|
@@ -228,15 +295,15 @@ Both robots and networks have inherent memory that persists across runs:
|
|
|
228
295
|
# Standalone robot with inherent memory
|
|
229
296
|
robot = RobotLab.build(name: "assistant", system_prompt: "You are helpful.")
|
|
230
297
|
|
|
231
|
-
robot.run(
|
|
232
|
-
robot.run(
|
|
298
|
+
robot.run("My name is Alice")
|
|
299
|
+
robot.run("What's my name?") # Memory persists automatically
|
|
233
300
|
|
|
234
301
|
# Access robot's memory
|
|
235
302
|
robot.memory[:user_id] = 123
|
|
236
303
|
robot.memory.data[:category] = "billing"
|
|
237
304
|
|
|
238
305
|
# Runtime memory injection
|
|
239
|
-
robot.run(
|
|
306
|
+
robot.run("Help me", memory: { session_id: "abc123", tier: "premium" })
|
|
240
307
|
|
|
241
308
|
# Reset memory when needed
|
|
242
309
|
robot.reset_memory
|
|
@@ -285,19 +352,109 @@ filesystem_server = {
|
|
|
285
352
|
robot = RobotLab.build(
|
|
286
353
|
name: "developer",
|
|
287
354
|
template: :developer,
|
|
288
|
-
|
|
355
|
+
mcp: [filesystem_server]
|
|
289
356
|
)
|
|
290
357
|
|
|
291
358
|
# Robot can now use filesystem tools
|
|
292
|
-
result = robot.run(
|
|
359
|
+
result = robot.run("List the files in the current directory")
|
|
293
360
|
```
|
|
294
361
|
|
|
362
|
+
## Message Bus
|
|
363
|
+
|
|
364
|
+
Robots can communicate bidirectionally via an optional message bus, independent of the Network pipeline. This enables negotiation loops, convergence patterns, and cyclic workflows.
|
|
365
|
+
|
|
366
|
+
Connect robots to a bus at construction time with `bus:`, or after creation with `with_bus`:
|
|
367
|
+
|
|
368
|
+
```ruby
|
|
369
|
+
require "robot_lab"
|
|
370
|
+
|
|
371
|
+
bus = TypedBus::MessageBus.new
|
|
372
|
+
|
|
373
|
+
class Comedian < RobotLab::Robot
|
|
374
|
+
def initialize(bus:)
|
|
375
|
+
super(name: "bob", template: :comedian, bus: bus)
|
|
376
|
+
on_message do |message|
|
|
377
|
+
joke = run(message.content.to_s).last_text_content.strip
|
|
378
|
+
reply(message, joke)
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
class ComedyCritic < RobotLab::Robot
|
|
384
|
+
def initialize(bus:)
|
|
385
|
+
super(name: "alice", template: :comedy_critic, bus: bus)
|
|
386
|
+
@accepted = false
|
|
387
|
+
on_message do |message|
|
|
388
|
+
verdict = run("Evaluate this joke:\n\n#{message.content}").last_text_content.strip
|
|
389
|
+
@accepted = verdict.start_with?("FUNNY")
|
|
390
|
+
send_message(to: :bob, content: "Try again.") unless @accepted
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
attr_reader :accepted
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
bob = Comedian.new(bus: bus)
|
|
397
|
+
alice = ComedyCritic.new(bus: bus)
|
|
398
|
+
alice.send_message(to: :bob, content: "Tell me a funny robot joke.")
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Key features:
|
|
402
|
+
|
|
403
|
+
- **Typed channels** — only `RobotMessage` objects are accepted (type enforcement via `typed_bus`)
|
|
404
|
+
- **Auto-ACK** — `on_message { |message| }` auto-acknowledges; use `|delivery, message|` for manual ACK/NACK
|
|
405
|
+
- **Reply correlation** — `reply(message, content)` tracks conversation threads via `in_reply_to`
|
|
406
|
+
- **Outbox tracking** — sent messages tracked in `robot.outbox` with status and replies
|
|
407
|
+
- **Independent of Network** — bus communication works without a Network pipeline
|
|
408
|
+
|
|
409
|
+
## Dynamic Robot Spawning
|
|
410
|
+
|
|
411
|
+
Robots can create new robots at runtime using `spawn`. The bus is created lazily — no upfront wiring required:
|
|
412
|
+
|
|
413
|
+
```ruby
|
|
414
|
+
require "robot_lab"
|
|
415
|
+
|
|
416
|
+
class Dispatcher < RobotLab::Robot
|
|
417
|
+
attr_reader :spawned
|
|
418
|
+
|
|
419
|
+
def initialize(bus: nil)
|
|
420
|
+
super(name: "dispatcher", system_prompt: "Decide which specialist to create.", bus: bus)
|
|
421
|
+
@spawned = {}
|
|
422
|
+
|
|
423
|
+
on_message do |message|
|
|
424
|
+
puts "Got reply from #{message.from}: #{message.content.to_s.lines.first&.strip}"
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
def dispatch(question)
|
|
429
|
+
# Spawn a specialist (reuse if already spawned)
|
|
430
|
+
specialist = @spawned["helper"] ||= spawn(
|
|
431
|
+
name: "helper",
|
|
432
|
+
system_prompt: "You answer questions concisely."
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
# Have the specialist work and reply
|
|
436
|
+
answer = specialist.run(question).last_text_content.strip
|
|
437
|
+
specialist.send_message(to: :dispatcher, content: answer)
|
|
438
|
+
end
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
dispatcher = Dispatcher.new
|
|
442
|
+
dispatcher.dispatch("What is the capital of France?")
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Key features:
|
|
446
|
+
|
|
447
|
+
- **`spawn`** — creates a child robot on the same bus; creates a bus lazily if none exists
|
|
448
|
+
- **`with_bus`** — connect a robot to a bus after creation (`bot.with_bus(existing_bus)`)
|
|
449
|
+
- **Fan-out** — multiple robots with the same name all receive messages sent to that name
|
|
450
|
+
- **No setup required** — bus and channels are created automatically on first use
|
|
451
|
+
|
|
295
452
|
## Streaming
|
|
296
453
|
|
|
297
|
-
|
|
454
|
+
Pass a block to `run` to receive real-time events during execution:
|
|
298
455
|
|
|
299
456
|
```ruby
|
|
300
|
-
result = robot.run(
|
|
457
|
+
result = robot.run("Tell me a story") do |event|
|
|
301
458
|
case event[:event]
|
|
302
459
|
when "text.delta"
|
|
303
460
|
print event[:data][:delta]
|
data/Rakefile
CHANGED
|
@@ -45,23 +45,92 @@ task :rubocop_fix do
|
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
namespace :examples do
|
|
48
|
+
# Map of subdirectory-based demos to their entry point scripts
|
|
49
|
+
SUBDIR_ENTRY_POINTS = {
|
|
50
|
+
"14_rusty_circuit" => "open_mic.rb",
|
|
51
|
+
"15_memory_network_and_bus" => "editorial_pipeline.rb"
|
|
52
|
+
}.freeze
|
|
53
|
+
|
|
48
54
|
desc "Run all examples"
|
|
49
55
|
task :all do
|
|
56
|
+
# Single-file examples
|
|
50
57
|
Dir.glob("examples/*.rb").sort.each do |example|
|
|
51
58
|
puts "\n#{'=' * 60}"
|
|
52
59
|
puts "Running: #{example}"
|
|
53
60
|
puts '=' * 60
|
|
54
61
|
ruby example
|
|
55
62
|
end
|
|
63
|
+
|
|
64
|
+
# Subdirectory-based demos
|
|
65
|
+
SUBDIR_ENTRY_POINTS.each do |dir, entry|
|
|
66
|
+
path = "examples/#{dir}/#{entry}"
|
|
67
|
+
next unless File.exist?(path)
|
|
68
|
+
|
|
69
|
+
puts "\n#{'=' * 60}"
|
|
70
|
+
puts "Running: #{path}"
|
|
71
|
+
puts '=' * 60
|
|
72
|
+
ruby path
|
|
73
|
+
end
|
|
56
74
|
end
|
|
57
75
|
|
|
58
76
|
desc "Run a specific example by number (e.g., rake examples:run[1])"
|
|
59
77
|
task :run, [:num] do |_t, args|
|
|
60
|
-
|
|
78
|
+
padded = args[:num].rjust(2, '0')
|
|
79
|
+
|
|
80
|
+
# Try single-file example first
|
|
81
|
+
example = Dir.glob("examples/#{padded}_*.rb").first
|
|
61
82
|
if example
|
|
62
83
|
ruby example
|
|
84
|
+
next
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Try subdirectory-based demo
|
|
88
|
+
dir = Dir.glob("examples/#{padded}_*/").first
|
|
89
|
+
if dir
|
|
90
|
+
dir_name = File.basename(dir)
|
|
91
|
+
entry = SUBDIR_ENTRY_POINTS[dir_name]
|
|
92
|
+
if entry && File.exist?(File.join(dir, entry))
|
|
93
|
+
ruby File.join(dir, entry)
|
|
94
|
+
else
|
|
95
|
+
puts "Example #{args[:num]} directory found (#{dir_name}) but no entry point configured"
|
|
96
|
+
end
|
|
63
97
|
else
|
|
64
98
|
puts "Example #{args[:num]} not found"
|
|
65
99
|
end
|
|
66
100
|
end
|
|
67
101
|
end
|
|
102
|
+
|
|
103
|
+
namespace :docs do
|
|
104
|
+
desc "Build all documentation (YARD and MkDocs)"
|
|
105
|
+
task build: %i[yard mkdocs]
|
|
106
|
+
|
|
107
|
+
desc "Clean generated documentation"
|
|
108
|
+
task :clean do
|
|
109
|
+
rm_rf "doc"
|
|
110
|
+
rm_rf "site"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
desc "Build YARD API documentation"
|
|
114
|
+
task :yard do
|
|
115
|
+
sh "yard doc"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
namespace :yard do
|
|
119
|
+
desc "Serve YARD documentation locally"
|
|
120
|
+
task :serve do
|
|
121
|
+
sh "yard server --reload"
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
desc "Build MkDocs documentation"
|
|
126
|
+
task :mkdocs do
|
|
127
|
+
sh "mkdocs build"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
namespace :mkdocs do
|
|
131
|
+
desc "Serve MkDocs documentation locally"
|
|
132
|
+
task :serve do
|
|
133
|
+
sh "mkdocs serve"
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
data/docs/api/core/index.md
CHANGED
|
@@ -43,10 +43,20 @@ classDiagram
|
|
|
43
43
|
+scoped(namespace)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
class RobotMessage {
|
|
47
|
+
+id: Integer
|
|
48
|
+
+from: String
|
|
49
|
+
+content: String
|
|
50
|
+
+in_reply_to: String
|
|
51
|
+
+key()
|
|
52
|
+
+reply?()
|
|
53
|
+
}
|
|
54
|
+
|
|
46
55
|
Network --> Robot : contains
|
|
47
56
|
Robot --> Tool : has
|
|
48
57
|
State --> Memory : has
|
|
49
58
|
Network --> State : uses
|
|
59
|
+
Robot ..> RobotMessage : sends/receives
|
|
50
60
|
```
|
|
51
61
|
|
|
52
62
|
## Classes
|
|
@@ -57,7 +67,9 @@ classDiagram
|
|
|
57
67
|
| [Network](network.md) | Container for robots with routing and orchestration |
|
|
58
68
|
| [State](state.md) | Conversation state with data, results, and memory |
|
|
59
69
|
| [Tool](tool.md) | Callable function with parameters and handler |
|
|
70
|
+
| [AskUser](tool.md#built-in-askuser) | Built-in tool for terminal-based user interaction |
|
|
60
71
|
| [Memory](memory.md) | Namespaced key-value store for sharing data |
|
|
72
|
+
| RobotMessage | Typed envelope for bus-based inter-robot communication |
|
|
61
73
|
|
|
62
74
|
## Quick Examples
|
|
63
75
|
|