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/docs/api/core/state.md
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
# StateProxy
|
|
2
|
-
|
|
3
|
-
!!! danger "There is no `RobotLab::State` class"
|
|
4
|
-
RobotLab has never had a `State` class. Runtime state lives in
|
|
5
|
-
[`RobotLab::Memory`](memory.md). The only state object is
|
|
6
|
-
**`RobotLab::StateProxy`** (`lib/robot_lab/state_proxy.rb`), which is what
|
|
7
|
-
`memory.data` returns — and that is what this page documents.
|
|
8
|
-
|
|
9
|
-
This page is listed as "State" in the site navigation for historical reasons.
|
|
10
|
-
For the key-value store, subscriptions, blocking reads, results, and
|
|
11
|
-
serialization, go to **[Memory](memory.md)**.
|
|
12
|
-
|
|
13
|
-
## Class: `RobotLab::StateProxy`
|
|
14
|
-
|
|
15
|
-
A thin wrapper around a symbol-keyed Hash that adds method-style access and an
|
|
16
|
-
optional change callback. It is not a `Hash` subclass and does not implement the
|
|
17
|
-
full `Hash` interface — the supported methods are listed below.
|
|
18
|
-
|
|
19
|
-
```ruby
|
|
20
|
-
proxy = RobotLab::StateProxy.new({ count: 0, name: "test" })
|
|
21
|
-
|
|
22
|
-
proxy[:count] = 1
|
|
23
|
-
proxy.count # => 1
|
|
24
|
-
proxy[:name] # => "test"
|
|
25
|
-
proxy.to_h # => { count: 1, name: "test" }
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Where you get one
|
|
29
|
-
|
|
30
|
-
`memory.data` — the reserved `:data` key of a `Memory` — is the only place the
|
|
31
|
-
framework hands you a `StateProxy`:
|
|
32
|
-
|
|
33
|
-
```ruby
|
|
34
|
-
memory = RobotLab.create_memory(data: { user_id: "123" })
|
|
35
|
-
|
|
36
|
-
memory.data # => #<RobotLab::StateProxy {user_id: "123"}>
|
|
37
|
-
memory.data[:user_id] # => "123" Hash access
|
|
38
|
-
memory.data.user_id # => "123" method access
|
|
39
|
-
memory.data[:status] = "active"
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
The proxy is memoized (`@data ||= StateProxy.new(...)`) and is reset whenever
|
|
43
|
-
`memory[:data] = ...` or `memory.reset` is called.
|
|
44
|
-
|
|
45
|
-
!!! warning "`memory.data` mutations are not reactive"
|
|
46
|
-
`memory.data[:x] = 1` writes through the proxy into the `:data` hash. It does
|
|
47
|
-
**not** go through `Memory#set`, so it wakes no blocking readers and notifies
|
|
48
|
-
no subscribers. Use `memory.set(:x, 1)` on a non-reserved key when you need
|
|
49
|
-
reactive semantics.
|
|
50
|
-
|
|
51
|
-
## Constructor
|
|
52
|
-
|
|
53
|
-
```ruby
|
|
54
|
-
RobotLab::StateProxy.new(data = {}, on_change: nil)
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
| Name | Type | Default | Description |
|
|
58
|
-
|------|------|---------|-------------|
|
|
59
|
-
| `data` | `Hash` | `{}` | Initial data. Keys are converted to Symbols via `transform_keys(&:to_sym)` |
|
|
60
|
-
| `on_change` | `Proc`, `nil` | `nil` | Called as `on_change.call(key, old_value, new_value)` on `[]=`, and only when the value actually changed (`old_value != value`) |
|
|
61
|
-
|
|
62
|
-
`Memory#data` constructs the proxy **without** an `on_change` callback.
|
|
63
|
-
|
|
64
|
-
## Methods
|
|
65
|
-
|
|
66
|
-
### Element access
|
|
67
|
-
|
|
68
|
-
```ruby
|
|
69
|
-
proxy[:key] # => value (keys are symbolized on read)
|
|
70
|
-
proxy[:key] = value # fires on_change when the value differs
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Both `[]` and `[]=` call `key.to_sym`, so `proxy["a"]` and `proxy[:a]` are the same entry.
|
|
74
|
-
|
|
75
|
-
### Method-style access
|
|
76
|
-
|
|
77
|
-
```ruby
|
|
78
|
-
proxy.name # same as proxy[:name] — only when :name already exists
|
|
79
|
-
proxy.name = "x" # same as proxy[:name] = "x" — always works
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Implemented with `method_missing`. A **getter** for a key that does not exist
|
|
83
|
-
falls through to `super` and raises `NoMethodError`; a **setter** always works and
|
|
84
|
-
creates the key. `respond_to?(:name)` is true only once the key exists.
|
|
85
|
-
|
|
86
|
-
Method-style access cannot reach a key whose name collides with a real
|
|
87
|
-
`StateProxy` method (`keys`, `size`, `map`, `each`, `delete`, `dup`, `inspect`, …).
|
|
88
|
-
Use `proxy[:keys]` for those.
|
|
89
|
-
|
|
90
|
-
### Full method list
|
|
91
|
-
|
|
92
|
-
| Method | Returns | Notes |
|
|
93
|
-
|--------|---------|-------|
|
|
94
|
-
| `[](key)` | value | Symbolizes the key |
|
|
95
|
-
| `[]=(key, value)` | value | Symbolizes the key; fires `on_change` |
|
|
96
|
-
| `key?(key)` | `Boolean` | Aliases: `has_key?`, `include?` |
|
|
97
|
-
| `keys` | `Array<Symbol>` | |
|
|
98
|
-
| `values` | `Array` | |
|
|
99
|
-
| `each { \|k, v\| }` | delegates to `Hash#each` | |
|
|
100
|
-
| `map { \|k, v\| }` | `Array` | |
|
|
101
|
-
| `delete(key)` | deleted value | Does **not** fire `on_change` |
|
|
102
|
-
| `merge!(other)` | `self` | Assigns each pair through `[]=`, so `on_change` fires per key |
|
|
103
|
-
| `to_h` | `Hash` | A **shallow** `dup`; aliased as `to_hash` |
|
|
104
|
-
| `dup` | `StateProxy` | Deep duplicate, preserving `on_change` |
|
|
105
|
-
| `empty?` | `Boolean` | |
|
|
106
|
-
| `size` | `Integer` | Aliased as `length` |
|
|
107
|
-
| `inspect` | `String` | `#<RobotLab::StateProxy {...}>` |
|
|
108
|
-
|
|
109
|
-
There is no `clear`, no `fetch`, no `dig`, and no `clone`. `to_h` is a shallow
|
|
110
|
-
copy, so nested Hashes are shared with the proxy — mutate them and the proxy sees
|
|
111
|
-
it. Use `dup` when you need isolation.
|
|
112
|
-
|
|
113
|
-
## Examples
|
|
114
|
-
|
|
115
|
-
### Nested data
|
|
116
|
-
|
|
117
|
-
```ruby
|
|
118
|
-
memory = RobotLab.create_memory(
|
|
119
|
-
data: { user: { name: "Alice", plan: "pro" } }
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
memory.data[:user][:name] # => "Alice" (plain Hash below the top level)
|
|
123
|
-
memory.data.user # => { name: "Alice", plan: "pro" }
|
|
124
|
-
memory.data.to_h # => { user: { name: "Alice", plan: "pro" } }
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
Only the top level is proxied. Nested values are returned as-is — a nested Hash
|
|
128
|
-
is a plain Hash, not another `StateProxy`.
|
|
129
|
-
|
|
130
|
-
### Change tracking
|
|
131
|
-
|
|
132
|
-
```ruby
|
|
133
|
-
proxy = RobotLab::StateProxy.new({ status: "pending" }, on_change: lambda { |key, old, new|
|
|
134
|
-
puts "#{key}: #{old.inspect} -> #{new.inspect}"
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
proxy[:status] = "active" # "status: \"pending\" -> \"active\""
|
|
138
|
-
proxy[:status] = "active" # silent — value unchanged
|
|
139
|
-
proxy.delete(:status) # silent — delete does not fire on_change
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
## See Also
|
|
143
|
-
|
|
144
|
-
- [Memory](memory.md) — the store that owns the proxy: keys, subscriptions, blocking reads, results, serialization
|
|
145
|
-
- [State Management Architecture](../../architecture/state-management.md)
|
data/docs/api/core/tool.md
DELETED
|
@@ -1,520 +0,0 @@
|
|
|
1
|
-
# Tool
|
|
2
|
-
|
|
3
|
-
Callable function that robots can use to interact with external systems.
|
|
4
|
-
|
|
5
|
-
## Class: `RobotLab::Tool < RubyLLM::Tool`
|
|
6
|
-
|
|
7
|
-
RobotLab::Tool inherits from RubyLLM::Tool, adding a `robot:` constructor parameter, a `Tool.create` factory for dynamic tools, and graceful error handling that returns plain-text errors to the LLM instead of crashing the run.
|
|
8
|
-
|
|
9
|
-
### Subclass Pattern
|
|
10
|
-
|
|
11
|
-
```ruby
|
|
12
|
-
class GetWeather < RobotLab::Tool
|
|
13
|
-
description "Get weather for a location"
|
|
14
|
-
|
|
15
|
-
param :location, type: "string", desc: "City name or zip code"
|
|
16
|
-
param :unit, type: "string", desc: "Temperature unit", required: false
|
|
17
|
-
|
|
18
|
-
def execute(location:, unit: "celsius")
|
|
19
|
-
WeatherService.current(location, unit: unit)
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
### Factory Pattern
|
|
25
|
-
|
|
26
|
-
```ruby
|
|
27
|
-
tool = RobotLab::Tool.create(
|
|
28
|
-
name: "get_weather",
|
|
29
|
-
description: "Get current weather for a location",
|
|
30
|
-
parameters: {
|
|
31
|
-
type: "object",
|
|
32
|
-
properties: {
|
|
33
|
-
location: { type: "string", description: "City name" }
|
|
34
|
-
},
|
|
35
|
-
required: ["location"]
|
|
36
|
-
}
|
|
37
|
-
) { |args| WeatherService.current(args[:location]) }
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
## Constructor
|
|
41
|
-
|
|
42
|
-
```ruby
|
|
43
|
-
Tool.new(robot: nil)
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
**Parameters:**
|
|
47
|
-
|
|
48
|
-
| Name | Type | Description |
|
|
49
|
-
|------|------|-------------|
|
|
50
|
-
| `robot` | `Robot, nil` | The owning robot instance |
|
|
51
|
-
|
|
52
|
-
## Constants
|
|
53
|
-
|
|
54
|
-
| Constant | Value | Description |
|
|
55
|
-
|----------|-------|-------------|
|
|
56
|
-
| `RobotLab::Robot::DEFAULT_MAX_TOOLS` | `128` | Ceiling on the number of tools a robot hands the provider per turn. Override per robot with `RunConfig#max_tools`; a nil, zero, or negative value falls back to 128, so the cap cannot be disabled |
|
|
57
|
-
|
|
58
|
-
## Class Methods
|
|
59
|
-
|
|
60
|
-
### raise_on_error / raise_on_error?
|
|
61
|
-
|
|
62
|
-
```ruby
|
|
63
|
-
MyTool.raise_on_error = true
|
|
64
|
-
MyTool.raise_on_error? # => true
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Per-class flag controlling whether `call` propagates exceptions from `execute` instead of catching them. Defaults to `false`.
|
|
68
|
-
|
|
69
|
-
!!! warning "`raise_on_error` does not walk the inheritance chain"
|
|
70
|
-
The reader is `defined?(@raise_on_error) ? @raise_on_error : false` — it
|
|
71
|
-
inspects only the receiving class's own instance variable. A subclass of a
|
|
72
|
-
class that set `self.raise_on_error = true` silently reverts to `false`:
|
|
73
|
-
|
|
74
|
-
```ruby
|
|
75
|
-
class Critical < RobotLab::Tool; self.raise_on_error = true; end
|
|
76
|
-
class Derived < Critical; end
|
|
77
|
-
|
|
78
|
-
Critical.raise_on_error? # => true
|
|
79
|
-
Derived.raise_on_error? # => false <-- errors are swallowed again
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Set the flag explicitly on every subclass that needs it. Contrast
|
|
83
|
-
[`ractor_safe`](#ractor_safe-ractor_safe), which *does* walk the chain.
|
|
84
|
-
|
|
85
|
-
### ractor_safe / ractor_safe?
|
|
86
|
-
|
|
87
|
-
```ruby
|
|
88
|
-
class Pure < RobotLab::Tool
|
|
89
|
-
ractor_safe true
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
Pure.ractor_safe? # => true
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
Declares that the tool class is safe to run inside a Ractor. With no argument it
|
|
96
|
-
acts as a getter; with a Boolean it sets the value. `ractor_safe?` is an alias
|
|
97
|
-
for `ractor_safe`.
|
|
98
|
-
|
|
99
|
-
Unlike `raise_on_error`, the getter **does** walk the inheritance chain: if the
|
|
100
|
-
class has no `@ractor_safe` of its own it asks its superclass, terminating at
|
|
101
|
-
`false`.
|
|
102
|
-
|
|
103
|
-
```ruby
|
|
104
|
-
class Pure < RobotLab::Tool; ractor_safe true; end
|
|
105
|
-
class Derived < Pure; end
|
|
106
|
-
|
|
107
|
-
Derived.ractor_safe? # => true — inherited
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
**Ractor pool dispatch.** `Tool#call` routes through the Ractor worker pool
|
|
111
|
-
(`RobotLab.ractor_pool.submit(self.class.name, args)`) only when **all three**
|
|
112
|
-
hold:
|
|
113
|
-
|
|
114
|
-
1. `self.class.ractor_safe?` is true
|
|
115
|
-
2. `self.class.name` is not `nil` (an anonymous class — including everything from
|
|
116
|
-
`Tool.create` — always falls through to the inline path)
|
|
117
|
-
3. `RobotLab.extension_loaded?(:ractor)` — i.e. the `robot_lab-ractor` gem is loaded
|
|
118
|
-
|
|
119
|
-
Otherwise `execute` runs inline on the calling thread. Ractor-safe tools must be
|
|
120
|
-
stateless: they are instantiated fresh inside the worker for each call.
|
|
121
|
-
|
|
122
|
-
### Tool.create
|
|
123
|
-
|
|
124
|
-
Factory for dynamic tools (MCP wrappers, inline tools).
|
|
125
|
-
|
|
126
|
-
```ruby
|
|
127
|
-
Tool.create(
|
|
128
|
-
name:,
|
|
129
|
-
description: nil,
|
|
130
|
-
parameters: nil,
|
|
131
|
-
mcp: nil,
|
|
132
|
-
robot: nil,
|
|
133
|
-
&handler
|
|
134
|
-
)
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
**Parameters:**
|
|
138
|
-
|
|
139
|
-
| Name | Type | Description |
|
|
140
|
-
|------|------|-------------|
|
|
141
|
-
| `name` | `String, Symbol` | Tool identifier |
|
|
142
|
-
| `description` | `String` | What the tool does |
|
|
143
|
-
| `parameters` | `Hash` | JSON Schema parameter definition |
|
|
144
|
-
| `mcp` | `String` | MCP server name; makes `mcp?` true |
|
|
145
|
-
| `robot` | `Robot` | Owning robot instance |
|
|
146
|
-
| `&handler` | `Block` | Receives the `args` hash, returns the result |
|
|
147
|
-
|
|
148
|
-
`create` builds an **anonymous** subclass (`Class.new(self)`) and stashes the
|
|
149
|
-
explicit name on the instance. Two consequences:
|
|
150
|
-
|
|
151
|
-
- `parameters` is read with **symbol** keys (`params_hash[:properties]`,
|
|
152
|
-
`pdef[:type]`, `pdef[:description]`, `params_hash[:required]`). A string-keyed
|
|
153
|
-
schema is silently ignored and the tool ends up with no parameters.
|
|
154
|
-
- Because `self.class.name` is `nil`, a `create`d tool never dispatches to the
|
|
155
|
-
Ractor pool even if you set `ractor_safe`.
|
|
156
|
-
|
|
157
|
-
## Inherited DSL (from RubyLLM::Tool)
|
|
158
|
-
|
|
159
|
-
### description
|
|
160
|
-
|
|
161
|
-
```ruby
|
|
162
|
-
class MyTool < RobotLab::Tool
|
|
163
|
-
description "What this tool does"
|
|
164
|
-
end
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### param
|
|
168
|
-
|
|
169
|
-
```ruby
|
|
170
|
-
class MyTool < RobotLab::Tool
|
|
171
|
-
param :name, type: "string", desc: "User's name"
|
|
172
|
-
param :age, type: "integer", desc: "User's age", required: false
|
|
173
|
-
end
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
`param` accepts exactly four options — anything else raises
|
|
177
|
-
`ArgumentError: unknown keyword`:
|
|
178
|
-
|
|
179
|
-
| Option | Type | Default | Description |
|
|
180
|
-
|--------|------|---------|-------------|
|
|
181
|
-
| `type` | `String` | `"string"` | JSON Schema type (`"string"`, `"integer"`, `"number"`, `"boolean"`, `"array"`, `"object"`) |
|
|
182
|
-
| `desc` | `String` | `nil` | Description shown to the LLM |
|
|
183
|
-
| `description` | `String` | `nil` | Synonym for `desc` |
|
|
184
|
-
| `required` | `Boolean` | `true` | Whether the LLM must supply the parameter |
|
|
185
|
-
|
|
186
|
-
!!! warning "There is no `enum:` option"
|
|
187
|
-
`param :unit, type: "string", enum: %w[c f]` raises
|
|
188
|
-
`ArgumentError: unknown keyword: :enum`. Constrain the value in the
|
|
189
|
-
description and validate it inside `execute`, or supply a full JSON Schema
|
|
190
|
-
through [`Tool.create(parameters:)`](#toolcreate).
|
|
191
|
-
|
|
192
|
-
### execute
|
|
193
|
-
|
|
194
|
-
```ruby
|
|
195
|
-
class MyTool < RobotLab::Tool
|
|
196
|
-
def execute(name:, age: nil)
|
|
197
|
-
# Implementation
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### halt
|
|
203
|
-
|
|
204
|
-
Stop the tool use loop from within execute:
|
|
205
|
-
|
|
206
|
-
```ruby
|
|
207
|
-
def execute(**)
|
|
208
|
-
halt("Done processing")
|
|
209
|
-
end
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
### with_params
|
|
213
|
-
|
|
214
|
-
Set provider-specific parameters:
|
|
215
|
-
|
|
216
|
-
```ruby
|
|
217
|
-
class MyTool < RobotLab::Tool
|
|
218
|
-
with_params(strict: true)
|
|
219
|
-
end
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
## Attributes
|
|
223
|
-
|
|
224
|
-
### robot
|
|
225
|
-
|
|
226
|
-
```ruby
|
|
227
|
-
tool.robot # => Robot or nil
|
|
228
|
-
tool.robot = some_robot
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
Read/write accessor for the owning robot. Set via constructor or assigned later.
|
|
232
|
-
|
|
233
|
-
### mcp
|
|
234
|
-
|
|
235
|
-
```ruby
|
|
236
|
-
tool.mcp # => String or nil
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
The MCP server name, set via `Tool.create(mcp: "server_name")`.
|
|
240
|
-
|
|
241
|
-
## Methods
|
|
242
|
-
|
|
243
|
-
### name
|
|
244
|
-
|
|
245
|
-
```ruby
|
|
246
|
-
tool.name # => String
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
Returns the tool name. For `create`d tools, the explicit `name:` you passed. For
|
|
250
|
-
subclasses, ruby_llm derives it from the class name — **including the namespace**,
|
|
251
|
-
with `::` rendered as `--`:
|
|
252
|
-
|
|
253
|
-
```ruby
|
|
254
|
-
RobotLab::AskUser.new.name # => "robot_lab--ask_user"
|
|
255
|
-
|
|
256
|
-
class Sub1 < RobotLab::Tool; end
|
|
257
|
-
Sub1.new.name # => "sub1" (top-level class, no namespace)
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
This matters when writing a `tools:` name allowlist: the allowlist must contain
|
|
261
|
-
the fully-derived name (`"robot_lab--ask_user"`), not the short one.
|
|
262
|
-
|
|
263
|
-
### mcp?
|
|
264
|
-
|
|
265
|
-
```ruby
|
|
266
|
-
tool.mcp? # => Boolean
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
Whether this is an MCP-provided tool.
|
|
270
|
-
|
|
271
|
-
### call
|
|
272
|
-
|
|
273
|
-
```ruby
|
|
274
|
-
result = tool.call(args_hash)
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
Overrides `RubyLLM::Tool#call`. The whole invocation is wrapped in the
|
|
278
|
-
`:tool_call` hook family, then dispatched either to the Ractor pool or inline
|
|
279
|
-
(see [`ractor_safe`](#ractor_safe-ractor_safe)); ruby_llm's `call` converts
|
|
280
|
-
string keys to symbols and invokes `execute(**args)`.
|
|
281
|
-
|
|
282
|
-
Errors are caught and returned to the LLM as a plain-text string it can reason
|
|
283
|
-
about. There are two distinct paths:
|
|
284
|
-
|
|
285
|
-
| Raised | Returned text | Logged? |
|
|
286
|
-
|--------|---------------|---------|
|
|
287
|
-
| `RobotLab::ToolError` | `Error (<name>): <message>` — plus `" (retryable)"` when `RobotLab::Errors.retryable?` is true | **No** |
|
|
288
|
-
| any other `StandardError` | `Error (<name>): <message>` | Yes — `RobotLab.config.logger.warn("Tool '<name>' error: <Class>: <message>")` |
|
|
289
|
-
|
|
290
|
-
```ruby
|
|
291
|
-
class T2 < RobotLab::Tool
|
|
292
|
-
description "t"
|
|
293
|
-
def execute(**) = raise RobotLab::ToolError.new("nope", retryable: true)
|
|
294
|
-
end
|
|
295
|
-
T2.new.call({}) # => "Error (t2): nope (retryable)" -- nothing logged
|
|
296
|
-
|
|
297
|
-
class T3 < RobotLab::Tool
|
|
298
|
-
description "t"
|
|
299
|
-
def execute(**) = raise ArgumentError, "bad"
|
|
300
|
-
end
|
|
301
|
-
T3.new.call({}) # => "Error (t3): bad" -- WARN logged
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
`" (retryable)"` is appended only for `ToolError`/`MCPError` instances
|
|
305
|
-
constructed with `retryable: true` — see [Retryable Errors](../errors.md#retryable-errors).
|
|
306
|
-
|
|
307
|
-
To propagate exceptions instead of catching them (for critical tools), set `raise_on_error` on the class:
|
|
308
|
-
|
|
309
|
-
```ruby
|
|
310
|
-
class CriticalTool < RobotLab::Tool
|
|
311
|
-
self.raise_on_error = true
|
|
312
|
-
# ...
|
|
313
|
-
end
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
`raise_on_error` applies to **both** paths, is per-class, defaults to `false`, and
|
|
317
|
-
[is not inherited by subclasses](#raise_on_error-raise_on_error).
|
|
318
|
-
|
|
319
|
-
### params_schema
|
|
320
|
-
|
|
321
|
-
```ruby
|
|
322
|
-
tool.params_schema # => Hash or nil
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
Inherited. Returns the JSON Schema for tool parameters.
|
|
326
|
-
|
|
327
|
-
### provider_params
|
|
328
|
-
|
|
329
|
-
```ruby
|
|
330
|
-
tool.provider_params # => Hash
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
Inherited. Returns provider-specific parameters (e.g., `{ strict: true }`).
|
|
334
|
-
|
|
335
|
-
### to_h
|
|
336
|
-
|
|
337
|
-
```ruby
|
|
338
|
-
tool.to_h # => Hash
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
Hash representation with `:name`, `:description`, `:mcp` — `.compact`ed, so
|
|
342
|
-
`:description` and `:mcp` are absent when nil:
|
|
343
|
-
|
|
344
|
-
```ruby
|
|
345
|
-
class T4 < RobotLab::Tool
|
|
346
|
-
description "t"
|
|
347
|
-
def execute(**) = nil
|
|
348
|
-
end
|
|
349
|
-
T4.new.to_h # => { name: "t4", description: "t" } -- no :mcp key
|
|
350
|
-
```
|
|
351
|
-
|
|
352
|
-
### to_json
|
|
353
|
-
|
|
354
|
-
```ruby
|
|
355
|
-
tool.to_json # => String
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
JSON representation.
|
|
359
|
-
|
|
360
|
-
### to_json_schema
|
|
361
|
-
|
|
362
|
-
```ruby
|
|
363
|
-
tool.to_json_schema # => Hash
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
JSON Schema representation for LLM function calling. Returns `{ name:, description:, parameters: }`.
|
|
367
|
-
|
|
368
|
-
## Robot-Aware Tools
|
|
369
|
-
|
|
370
|
-
Tools that modify their owning robot use the `robot` accessor:
|
|
371
|
-
|
|
372
|
-
```ruby
|
|
373
|
-
class AdjustTemperature < RobotLab::Tool
|
|
374
|
-
description "Adjust the robot's creativity level"
|
|
375
|
-
|
|
376
|
-
param :level, type: "number", desc: "Temperature from 0.0 to 1.0"
|
|
377
|
-
|
|
378
|
-
def execute(level:)
|
|
379
|
-
robot.with_temperature(level)
|
|
380
|
-
"Temperature adjusted to #{level}"
|
|
381
|
-
end
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
# The robot must exist before the tool can reference it, so attach afterwards
|
|
385
|
-
robot = RobotLab.build(name: "creative_bot", system_prompt: "You are creative.")
|
|
386
|
-
robot.local_tools << AdjustTemperature.new(robot: robot)
|
|
387
|
-
|
|
388
|
-
robot.run("Be more creative.", tools: :inherit)
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
Passing the bare **class** (`local_tools: [AdjustTemperature]`) leaves
|
|
392
|
-
`tool.robot` as `nil` — ruby_llm instantiates it with no arguments — and
|
|
393
|
-
`robot.with_temperature` then raises `NoMethodError` on nil. Instantiate with
|
|
394
|
-
`robot:` whenever the tool needs its owner.
|
|
395
|
-
|
|
396
|
-
## Parameter Types
|
|
397
|
-
|
|
398
|
-
### String
|
|
399
|
-
|
|
400
|
-
```ruby
|
|
401
|
-
param :name, type: "string", desc: "User's full name"
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
### Integer
|
|
405
|
-
|
|
406
|
-
```ruby
|
|
407
|
-
param :count, type: "integer", desc: "Number of results"
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
### Number (Float)
|
|
411
|
-
|
|
412
|
-
```ruby
|
|
413
|
-
param :price, type: "number", desc: "Price in dollars"
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
### Boolean
|
|
417
|
-
|
|
418
|
-
```ruby
|
|
419
|
-
param :active, type: "boolean", desc: "Whether the user is active"
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
### Required vs Optional
|
|
423
|
-
|
|
424
|
-
Parameters are required by default. Mark optional with `required: false`:
|
|
425
|
-
|
|
426
|
-
```ruby
|
|
427
|
-
param :query, type: "string", desc: "Search query" # required
|
|
428
|
-
param :limit, type: "integer", desc: "Max results", required: false # optional
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
## Built-in: AskUser
|
|
432
|
-
|
|
433
|
-
`RobotLab::AskUser` is a built-in tool that lets a robot ask the user a question via the terminal. The LLM decides when human input is needed and calls this tool.
|
|
434
|
-
|
|
435
|
-
### Class: `RobotLab::AskUser < RobotLab::Tool`
|
|
436
|
-
|
|
437
|
-
```ruby
|
|
438
|
-
class RobotLab::AskUser < RobotLab::Tool
|
|
439
|
-
description "Ask the user a question and wait for their typed response"
|
|
440
|
-
param :question, type: "string", desc: "The question to ask the user"
|
|
441
|
-
param :choices, type: "array", desc: "Optional list of choices to present", required: false
|
|
442
|
-
param :default, type: "string", desc: "Default value if user presses Enter", required: false
|
|
443
|
-
end
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
### Parameters
|
|
447
|
-
|
|
448
|
-
| Name | Type | Required | Description |
|
|
449
|
-
|------|------|----------|-------------|
|
|
450
|
-
| `question` | `String` | Yes | The question to display |
|
|
451
|
-
| `choices` | `Array` | No | Numbered choices to present |
|
|
452
|
-
| `default` | `String` | No | Value returned when user presses Enter without typing |
|
|
453
|
-
|
|
454
|
-
Its LLM-facing name is **`robot_lab--ask_user`** (namespace included), not `ask_user`.
|
|
455
|
-
|
|
456
|
-
### IO Resolution
|
|
457
|
-
|
|
458
|
-
The tool reads input and writes output using the owning robot's `input`/`output` accessors:
|
|
459
|
-
|
|
460
|
-
1. `robot.input` / `robot.output` if set
|
|
461
|
-
2. Falls back to `$stdin` / `$stdout`
|
|
462
|
-
|
|
463
|
-
The prompt label is `robot&.name || "Robot"`.
|
|
464
|
-
|
|
465
|
-
!!! warning "Attach an instance, not the class"
|
|
466
|
-
`local_tools: [RobotLab::AskUser]` gives ruby_llm a bare class, which it
|
|
467
|
-
instantiates with no arguments — so `tool.robot` is `nil`, IO falls back to
|
|
468
|
-
`$stdin`/`$stdout`, and every prompt is labelled `[Robot]`. Pass
|
|
469
|
-
`RobotLab::AskUser.new(robot: robot)` to get the robot's own name and streams.
|
|
470
|
-
|
|
471
|
-
### Terminal Output
|
|
472
|
-
|
|
473
|
-
```
|
|
474
|
-
[interviewer] What programming language do you want to learn?
|
|
475
|
-
1. Ruby
|
|
476
|
-
2. Python
|
|
477
|
-
3. Go
|
|
478
|
-
> [Ruby]
|
|
479
|
-
```
|
|
480
|
-
|
|
481
|
-
### Usage
|
|
482
|
-
|
|
483
|
-
```ruby
|
|
484
|
-
robot = RobotLab.build(
|
|
485
|
-
name: "interviewer",
|
|
486
|
-
system_prompt: "Interview the user about their project needs. " \
|
|
487
|
-
"Use robot_lab--ask_user to gather information."
|
|
488
|
-
)
|
|
489
|
-
robot.local_tools << RobotLab::AskUser.new(robot: robot)
|
|
490
|
-
|
|
491
|
-
# run() defaults to tools: :none — :inherit is required for the tool to be sent
|
|
492
|
-
robot.run("Find out what the user wants to build", tools: :inherit)
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
### Testing with StringIO
|
|
496
|
-
|
|
497
|
-
```ruby
|
|
498
|
-
robot = RobotLab::Robot.new(name: "bot", template: :assistant)
|
|
499
|
-
robot.input = StringIO.new("Ruby\n")
|
|
500
|
-
robot.output = StringIO.new
|
|
501
|
-
|
|
502
|
-
tool = RobotLab::AskUser.new(robot: robot)
|
|
503
|
-
result = tool.call("question" => "Pick a language:", "choices" => ["Ruby", "Python"])
|
|
504
|
-
# => "Ruby"
|
|
505
|
-
```
|
|
506
|
-
|
|
507
|
-
### Choice Mapping
|
|
508
|
-
|
|
509
|
-
When `choices` are provided, the user can type either:
|
|
510
|
-
|
|
511
|
-
- A **number** (e.g., `2`) — mapped to the corresponding choice text
|
|
512
|
-
- **Text** (e.g., `Python`) — returned as-is
|
|
513
|
-
|
|
514
|
-
Out-of-range numbers are returned as-is (the LLM can re-ask if needed).
|
|
515
|
-
|
|
516
|
-
## See Also
|
|
517
|
-
|
|
518
|
-
- [Using Tools Guide](../../guides/using-tools.md)
|
|
519
|
-
- [Robot](robot.md)
|
|
520
|
-
- [MCP Integration](../../guides/mcp-integration.md)
|