robot_lab 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. checksums.yaml +7 -0
  2. data/.envrc +1 -0
  3. data/.github/workflows/deploy-github-pages.yml +52 -0
  4. data/.github/workflows/deploy-yard-docs.yml +52 -0
  5. data/CHANGELOG.md +55 -0
  6. data/COMMITS.md +196 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +332 -0
  9. data/Rakefile +67 -0
  10. data/docs/api/adapters/anthropic.md +121 -0
  11. data/docs/api/adapters/gemini.md +133 -0
  12. data/docs/api/adapters/index.md +104 -0
  13. data/docs/api/adapters/openai.md +134 -0
  14. data/docs/api/core/index.md +113 -0
  15. data/docs/api/core/memory.md +314 -0
  16. data/docs/api/core/network.md +291 -0
  17. data/docs/api/core/robot.md +273 -0
  18. data/docs/api/core/state.md +273 -0
  19. data/docs/api/core/tool.md +353 -0
  20. data/docs/api/history/active-record-adapter.md +195 -0
  21. data/docs/api/history/config.md +191 -0
  22. data/docs/api/history/index.md +132 -0
  23. data/docs/api/history/thread-manager.md +144 -0
  24. data/docs/api/index.md +82 -0
  25. data/docs/api/mcp/client.md +221 -0
  26. data/docs/api/mcp/index.md +111 -0
  27. data/docs/api/mcp/server.md +225 -0
  28. data/docs/api/mcp/transports.md +264 -0
  29. data/docs/api/messages/index.md +67 -0
  30. data/docs/api/messages/text-message.md +102 -0
  31. data/docs/api/messages/tool-call-message.md +144 -0
  32. data/docs/api/messages/tool-result-message.md +154 -0
  33. data/docs/api/messages/user-message.md +171 -0
  34. data/docs/api/streaming/context.md +174 -0
  35. data/docs/api/streaming/events.md +237 -0
  36. data/docs/api/streaming/index.md +108 -0
  37. data/docs/architecture/core-concepts.md +243 -0
  38. data/docs/architecture/index.md +138 -0
  39. data/docs/architecture/message-flow.md +320 -0
  40. data/docs/architecture/network-orchestration.md +216 -0
  41. data/docs/architecture/robot-execution.md +243 -0
  42. data/docs/architecture/state-management.md +323 -0
  43. data/docs/assets/css/custom.css +56 -0
  44. data/docs/assets/images/robot_lab.jpg +0 -0
  45. data/docs/concepts.md +216 -0
  46. data/docs/examples/basic-chat.md +193 -0
  47. data/docs/examples/index.md +129 -0
  48. data/docs/examples/mcp-server.md +290 -0
  49. data/docs/examples/multi-robot-network.md +312 -0
  50. data/docs/examples/rails-application.md +420 -0
  51. data/docs/examples/tool-usage.md +310 -0
  52. data/docs/getting-started/configuration.md +230 -0
  53. data/docs/getting-started/index.md +56 -0
  54. data/docs/getting-started/installation.md +179 -0
  55. data/docs/getting-started/quick-start.md +203 -0
  56. data/docs/guides/building-robots.md +376 -0
  57. data/docs/guides/creating-networks.md +366 -0
  58. data/docs/guides/history.md +359 -0
  59. data/docs/guides/index.md +68 -0
  60. data/docs/guides/mcp-integration.md +356 -0
  61. data/docs/guides/memory.md +309 -0
  62. data/docs/guides/rails-integration.md +432 -0
  63. data/docs/guides/streaming.md +314 -0
  64. data/docs/guides/using-tools.md +394 -0
  65. data/docs/index.md +160 -0
  66. data/examples/01_simple_robot.rb +38 -0
  67. data/examples/02_tools.rb +106 -0
  68. data/examples/03_network.rb +103 -0
  69. data/examples/04_mcp.rb +219 -0
  70. data/examples/05_streaming.rb +124 -0
  71. data/examples/06_prompt_templates.rb +324 -0
  72. data/examples/07_network_memory.rb +329 -0
  73. data/examples/prompts/assistant/system.txt.erb +2 -0
  74. data/examples/prompts/assistant/user.txt.erb +1 -0
  75. data/examples/prompts/billing/system.txt.erb +7 -0
  76. data/examples/prompts/billing/user.txt.erb +1 -0
  77. data/examples/prompts/classifier/system.txt.erb +4 -0
  78. data/examples/prompts/classifier/user.txt.erb +1 -0
  79. data/examples/prompts/entity_extractor/system.txt.erb +11 -0
  80. data/examples/prompts/entity_extractor/user.txt.erb +3 -0
  81. data/examples/prompts/escalation/system.txt.erb +35 -0
  82. data/examples/prompts/escalation/user.txt.erb +34 -0
  83. data/examples/prompts/general/system.txt.erb +4 -0
  84. data/examples/prompts/general/user.txt.erb +1 -0
  85. data/examples/prompts/github_assistant/system.txt.erb +6 -0
  86. data/examples/prompts/github_assistant/user.txt.erb +1 -0
  87. data/examples/prompts/helper/system.txt.erb +1 -0
  88. data/examples/prompts/helper/user.txt.erb +1 -0
  89. data/examples/prompts/keyword_extractor/system.txt.erb +8 -0
  90. data/examples/prompts/keyword_extractor/user.txt.erb +3 -0
  91. data/examples/prompts/order_support/system.txt.erb +27 -0
  92. data/examples/prompts/order_support/user.txt.erb +22 -0
  93. data/examples/prompts/product_support/system.txt.erb +30 -0
  94. data/examples/prompts/product_support/user.txt.erb +32 -0
  95. data/examples/prompts/sentiment_analyzer/system.txt.erb +9 -0
  96. data/examples/prompts/sentiment_analyzer/user.txt.erb +3 -0
  97. data/examples/prompts/synthesizer/system.txt.erb +14 -0
  98. data/examples/prompts/synthesizer/user.txt.erb +15 -0
  99. data/examples/prompts/technical/system.txt.erb +7 -0
  100. data/examples/prompts/technical/user.txt.erb +1 -0
  101. data/examples/prompts/triage/system.txt.erb +16 -0
  102. data/examples/prompts/triage/user.txt.erb +17 -0
  103. data/lib/generators/robot_lab/install_generator.rb +78 -0
  104. data/lib/generators/robot_lab/robot_generator.rb +55 -0
  105. data/lib/generators/robot_lab/templates/initializer.rb.tt +41 -0
  106. data/lib/generators/robot_lab/templates/migration.rb.tt +32 -0
  107. data/lib/generators/robot_lab/templates/result_model.rb.tt +52 -0
  108. data/lib/generators/robot_lab/templates/robot.rb.tt +46 -0
  109. data/lib/generators/robot_lab/templates/robot_test.rb.tt +32 -0
  110. data/lib/generators/robot_lab/templates/routing_robot.rb.tt +53 -0
  111. data/lib/generators/robot_lab/templates/thread_model.rb.tt +40 -0
  112. data/lib/robot_lab/adapters/anthropic.rb +163 -0
  113. data/lib/robot_lab/adapters/base.rb +85 -0
  114. data/lib/robot_lab/adapters/gemini.rb +193 -0
  115. data/lib/robot_lab/adapters/openai.rb +159 -0
  116. data/lib/robot_lab/adapters/registry.rb +81 -0
  117. data/lib/robot_lab/configuration.rb +143 -0
  118. data/lib/robot_lab/error.rb +32 -0
  119. data/lib/robot_lab/errors.rb +70 -0
  120. data/lib/robot_lab/history/active_record_adapter.rb +146 -0
  121. data/lib/robot_lab/history/config.rb +115 -0
  122. data/lib/robot_lab/history/thread_manager.rb +93 -0
  123. data/lib/robot_lab/mcp/client.rb +210 -0
  124. data/lib/robot_lab/mcp/server.rb +84 -0
  125. data/lib/robot_lab/mcp/transports/base.rb +56 -0
  126. data/lib/robot_lab/mcp/transports/sse.rb +117 -0
  127. data/lib/robot_lab/mcp/transports/stdio.rb +133 -0
  128. data/lib/robot_lab/mcp/transports/streamable_http.rb +139 -0
  129. data/lib/robot_lab/mcp/transports/websocket.rb +108 -0
  130. data/lib/robot_lab/memory.rb +882 -0
  131. data/lib/robot_lab/memory_change.rb +123 -0
  132. data/lib/robot_lab/message.rb +357 -0
  133. data/lib/robot_lab/network.rb +350 -0
  134. data/lib/robot_lab/rails/engine.rb +29 -0
  135. data/lib/robot_lab/rails/railtie.rb +42 -0
  136. data/lib/robot_lab/robot.rb +560 -0
  137. data/lib/robot_lab/robot_result.rb +205 -0
  138. data/lib/robot_lab/robotic_model.rb +324 -0
  139. data/lib/robot_lab/state_proxy.rb +188 -0
  140. data/lib/robot_lab/streaming/context.rb +144 -0
  141. data/lib/robot_lab/streaming/events.rb +95 -0
  142. data/lib/robot_lab/streaming/sequence_counter.rb +48 -0
  143. data/lib/robot_lab/task.rb +117 -0
  144. data/lib/robot_lab/tool.rb +223 -0
  145. data/lib/robot_lab/tool_config.rb +112 -0
  146. data/lib/robot_lab/tool_manifest.rb +234 -0
  147. data/lib/robot_lab/user_message.rb +118 -0
  148. data/lib/robot_lab/version.rb +5 -0
  149. data/lib/robot_lab/waiter.rb +73 -0
  150. data/lib/robot_lab.rb +195 -0
  151. data/mkdocs.yml +214 -0
  152. data/sig/robot_lab.rbs +4 -0
  153. metadata +442 -0
data/README.md ADDED
@@ -0,0 +1,332 @@
1
+ # RobotLab
2
+
3
+ > [!CAUTION]
4
+ > This gem is under active development. APIs and features may change without notice. See the [CHANGELOG](CHANGELOG.md) for details.
5
+ <br>
6
+ <table>
7
+ <tr>
8
+ <td width="50%" align="center" valign="top">
9
+ <img src="docs/assets/images/robot_lab.jpg" alt="RobotLab"><br>
10
+ <em>"Build robots. Solve problems."</em>
11
+ </td>
12
+ <td width="50%" valign="top">
13
+ <strong>Multi-robot LLM workflow orchestration for Ruby</strong><br><br>
14
+ RobotLab enables you to build sophisticated AI applications using multiple specialized robots (LLM agents) that work together to accomplish complex tasks. Each robot has its own system prompt, tools, and capabilities.<br><br>
15
+ <strong>Key Features</strong><br>
16
+
17
+ - <strong>Multi-Robot Architecture</strong> - Build with specialized AI agents<br>
18
+ - <strong>Network Orchestration</strong> - Connect robots with flexible routing<br>
19
+ - <strong>Extensible Tools</strong> - Give robots custom capabilities<br>
20
+ - <strong>MCP Integration</strong> - Connect to external tool servers<br>
21
+ - <strong>Shared Memory</strong> - Hierarchical memory with namespaced scopes<br>
22
+ - <strong>Conversation History</strong> - Persist and restore threads<br>
23
+ - <strong>Streaming</strong> - Real-time event streaming<br>
24
+ - <strong>Rails Integration</strong> - Generators and ActiveRecord support
25
+ </td>
26
+ </tr>
27
+ </table>
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ bundle add robot_lab
33
+ ```
34
+
35
+ Or install it directly:
36
+
37
+ ```bash
38
+ gem install robot_lab
39
+ ```
40
+
41
+ ### Requirements
42
+
43
+ - Ruby >= 3.2
44
+ - [One or more API Keys for LLM providers supported by RubyLLM](https://rubyllm.com/configuration/#provider-configuration)
45
+
46
+ For comprehensive guides and API documentation, visit **[https://madbomber.github.io/robot_lab](https://madbomber.github.io/robot_lab)**
47
+
48
+ ## Getting Started
49
+
50
+ The simplest way to create a robot is with an inline `system_prompt`. This approach is ideal for development, testing, and quick prototyping:
51
+
52
+ ```ruby
53
+ require "robot_lab"
54
+
55
+ # Configure RobotLab
56
+ RobotLab.configure do |config|
57
+ config.default_model = "claude-sonnet-4"
58
+ end
59
+
60
+ # Create a robot with an inline system prompt
61
+ robot = RobotLab.build(
62
+ name: "assistant",
63
+ system_prompt: "You are a helpful assistant. Be concise and friendly."
64
+ )
65
+
66
+ # Run the robot
67
+ result = robot.run(message: "What is the capital of France?")
68
+
69
+ puts result.output.first.content
70
+ # => "The capital of France is Paris."
71
+ ```
72
+
73
+ ### Using Templates
74
+
75
+ For production applications, RobotLab supports a powerful template system built on ERB. Templates allow you to:
76
+
77
+ - **Compose prompts** from reusable components
78
+ - **Inject dynamic context** at build-time and run-time
79
+ - **Version control** your prompts alongside your code
80
+ - **Share prompts** across multiple robots
81
+
82
+ Configure the template directory:
83
+
84
+ ```ruby
85
+ RobotLab.configure do |config|
86
+ config.template_path = "app/prompts"
87
+ end
88
+ ```
89
+
90
+ Each template is a **directory** containing ERB files for different message roles:
91
+
92
+ ```
93
+ app/prompts/
94
+ assistant/
95
+ ├── system.txt.erb # System message (required)
96
+ ├── user.txt.erb # User prompt template (optional)
97
+ ├── assistant.txt.erb # Pre-filled assistant response (optional)
98
+ └── schema.rb # Structured output schema (optional)
99
+ ```
100
+
101
+ Create the system message at `app/prompts/assistant/system.txt.erb`:
102
+
103
+ ```erb
104
+ You are a helpful assistant for <%= company_name %>.
105
+
106
+ Your responsibilities:
107
+ - Answer questions accurately and concisely
108
+ - Be friendly and professional
109
+ - Admit when you don't know something
110
+
111
+ <% if guidelines %>
112
+ Additional guidelines:
113
+ <%= guidelines %>
114
+ <% end %>
115
+ ```
116
+
117
+ Reference the template directory using a Symbol:
118
+
119
+ ```ruby
120
+ robot = RobotLab.build(
121
+ name: "assistant",
122
+ template: :assistant,
123
+ context: { company_name: "Acme Corp", guidelines: nil }
124
+ )
125
+ ```
126
+
127
+ ### Combining Templates with System Prompts
128
+
129
+ 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:
130
+
131
+ ```ruby
132
+ robot = RobotLab.build(
133
+ name: "assistant",
134
+ template: :assistant,
135
+ context: { company_name: "Acme Corp" },
136
+ system_prompt: "DEBUG MODE: Log all tool calls. Today's date is #{Date.today}."
137
+ )
138
+ ```
139
+
140
+ ## Creating a Robot with Tools
141
+
142
+ ```ruby
143
+ # Define tools using RubyLLM::Tool
144
+ class Magic8Ball < RubyLLM::Tool
145
+ description "Consult the mystical Magic 8-Ball for guidance on yes/no questions"
146
+
147
+ param :question, type: "string", desc: "A yes/no question to ask the oracle"
148
+
149
+ RESPONSES = [
150
+ { answer: "It is certain", certainty: 0.95, vibe: "positive" },
151
+ { answer: "Ask again later", certainty: 0.10, vibe: "evasive" },
152
+ { answer: "Don't count on it", certainty: 0.85, vibe: "negative" },
153
+ { answer: "Signs point to yes", certainty: 0.75, vibe: "positive" },
154
+ { answer: "Reply hazy, try again", certainty: 0.05, vibe: "evasive" },
155
+ { answer: "My sources say no", certainty: 0.80, vibe: "negative" },
156
+ { answer: "Outlook good", certainty: 0.70, vibe: "positive" },
157
+ { answer: "Cannot predict now", certainty: 0.00, vibe: "evasive" }
158
+ ].freeze
159
+
160
+ def execute(question:)
161
+ response = RESPONSES.sample
162
+ { question: question, **response }
163
+ end
164
+ end
165
+
166
+ # Create robot with tools
167
+ robot = RobotLab.build(
168
+ name: "oracle",
169
+ system_prompt: "You are a mystical oracle. Use the Magic 8-Ball to answer questions about the future.",
170
+ tools: [Magic8Ball]
171
+ )
172
+
173
+ result = robot.run(message: "Should I start learning Rust?")
174
+ ```
175
+
176
+ ## Orchestrating Multiple Robots
177
+
178
+ Networks use [SimpleFlow](https://github.com/MadBomber/simple_flow) pipelines with optional task activation for intelligent routing:
179
+
180
+ ```ruby
181
+ # Custom classifier that activates the appropriate specialist
182
+ class ClassifierRobot < RobotLab::Robot
183
+ def call(result)
184
+ robot_result = run(**extract_run_context(result))
185
+
186
+ new_result = result
187
+ .with_context(@name.to_sym, robot_result)
188
+ .continue(robot_result)
189
+
190
+ # Route based on classification
191
+ category = robot_result.last_text_content.to_s.strip.downcase
192
+ case category
193
+ when /billing/ then new_result.activate(:billing)
194
+ when /technical/ then new_result.activate(:technical)
195
+ else new_result.activate(:general)
196
+ end
197
+ end
198
+ end
199
+
200
+ # Create specialized robots
201
+ classifier = ClassifierRobot.new(
202
+ name: "classifier",
203
+ template: :classifier
204
+ )
205
+
206
+ billing_robot = RobotLab.build(name: "billing", template: :billing)
207
+ technical_robot = RobotLab.build(name: "technical", template: :technical)
208
+ general_robot = RobotLab.build(name: "general", template: :general)
209
+
210
+ # Create network with optional task routing
211
+ network = RobotLab.create_network(name: "support") do
212
+ task :classifier, classifier, depends_on: :none
213
+ task :billing, billing_robot, depends_on: :optional
214
+ task :technical, technical_robot, depends_on: :optional
215
+ task :general, general_robot, depends_on: :optional
216
+ end
217
+
218
+ # Run the network
219
+ result = network.run(message: "I was charged twice for my subscription")
220
+ puts result.value.last_text_content
221
+ ```
222
+
223
+ ## Memory
224
+
225
+ Both robots and networks have inherent memory that persists across runs:
226
+
227
+ ```ruby
228
+ # Standalone robot with inherent memory
229
+ robot = RobotLab.build(name: "assistant", system_prompt: "You are helpful.")
230
+
231
+ robot.run(message: "My name is Alice")
232
+ robot.run(message: "What's my name?") # Memory persists automatically
233
+
234
+ # Access robot's memory
235
+ robot.memory[:user_id] = 123
236
+ robot.memory.data[:category] = "billing"
237
+
238
+ # Runtime memory injection
239
+ robot.run(message: "Help me", memory: { session_id: "abc123", tier: "premium" })
240
+
241
+ # Reset memory when needed
242
+ robot.reset_memory
243
+ ```
244
+
245
+ Networks pass context through SimpleFlow::Result:
246
+
247
+ ```ruby
248
+ # Create network with specialized robots
249
+ network = RobotLab.create_network(name: "support") do
250
+ task :classifier, classifier, depends_on: :none
251
+ task :billing, billing_robot, depends_on: :optional
252
+ end
253
+
254
+ # Run with context - available to all robots
255
+ result = network.run(
256
+ message: "I have a billing question",
257
+ customer_id: 456,
258
+ ticket_id: "TKT-123"
259
+ )
260
+
261
+ # Access results from specific robots
262
+ classifier_result = result.context[:classifier]
263
+ billing_result = result.context[:billing]
264
+
265
+ # The final value is the last robot's output
266
+ puts result.value.last_text_content
267
+ ```
268
+
269
+ ## MCP Integration
270
+
271
+ Connect to external tool servers via Model Context Protocol:
272
+
273
+ ```ruby
274
+ # Configure MCP server
275
+ filesystem_server = {
276
+ name: "filesystem",
277
+ transport: {
278
+ type: "stdio",
279
+ command: "mcp-server-filesystem",
280
+ args: ["/path/to/allowed/directory"]
281
+ }
282
+ }
283
+
284
+ # Create robot with MCP server - tools are auto-discovered
285
+ robot = RobotLab.build(
286
+ name: "developer",
287
+ template: :developer,
288
+ mcp_servers: [filesystem_server]
289
+ )
290
+
291
+ # Robot can now use filesystem tools
292
+ result = robot.run(message: "List the files in the current directory")
293
+ ```
294
+
295
+ ## Streaming
296
+
297
+ Subscribe to real-time events during execution:
298
+
299
+ ```ruby
300
+ result = robot.run(message: "Tell me a story") do |event|
301
+ case event[:event]
302
+ when "text.delta"
303
+ print event[:data][:delta]
304
+ when "run.completed"
305
+ puts "\nDone!"
306
+ end
307
+ end
308
+ ```
309
+
310
+ ## Rails Integration
311
+
312
+ ```bash
313
+ rails generate robot_lab:install
314
+ rails db:migrate
315
+ ```
316
+
317
+ This creates:
318
+ - `config/initializers/robot_lab.rb` - Configuration
319
+ - `app/robots/` - Directory for your robots
320
+ - Database tables for conversation history
321
+
322
+ ## Documentation
323
+
324
+ Full documentation is available at **[https://madbomber.github.io/robot_lab](https://madbomber.github.io/robot_lab)**
325
+
326
+ ## License
327
+
328
+ MIT License - Copyright (c) 2025 Dewayne VanHoozer
329
+
330
+ ## Contributing
331
+
332
+ Bug reports and pull requests are welcome on GitHub at https://github.com/MadBomber/robot_lab.
data/Rakefile ADDED
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ t.verbose = true
11
+ # Load test_helper before any tests run to ensure SimpleCov starts first
12
+ t.ruby_opts << "-rtest_helper"
13
+ end
14
+
15
+ task default: :test
16
+
17
+ desc "Run tests with verbose output"
18
+ task :test_verbose do
19
+ ENV["TESTOPTS"] = "--verbose"
20
+ Rake::Task[:test].invoke
21
+ end
22
+
23
+ desc "Run a single test file"
24
+ task :test_file, [:file] do |_t, args|
25
+ ruby "test/#{args[:file]}"
26
+ end
27
+
28
+ desc "Run integration tests only"
29
+ Rake::TestTask.new(:integration) do |t|
30
+ t.libs << "test"
31
+ t.libs << "lib"
32
+ t.test_files = FileList["test/integration/**/*_test.rb"]
33
+ t.verbose = true
34
+ t.ruby_opts << "-rtest_helper"
35
+ end
36
+
37
+ desc "Check code style with RuboCop"
38
+ task :rubocop do
39
+ sh "bundle exec rubocop"
40
+ end
41
+
42
+ desc "Auto-correct RuboCop offenses"
43
+ task :rubocop_fix do
44
+ sh "bundle exec rubocop -a"
45
+ end
46
+
47
+ namespace :examples do
48
+ desc "Run all examples"
49
+ task :all do
50
+ Dir.glob("examples/*.rb").sort.each do |example|
51
+ puts "\n#{'=' * 60}"
52
+ puts "Running: #{example}"
53
+ puts '=' * 60
54
+ ruby example
55
+ end
56
+ end
57
+
58
+ desc "Run a specific example by number (e.g., rake examples:run[1])"
59
+ task :run, [:num] do |_t, args|
60
+ example = Dir.glob("examples/#{args[:num].rjust(2, '0')}_*.rb").first
61
+ if example
62
+ ruby example
63
+ else
64
+ puts "Example #{args[:num]} not found"
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,121 @@
1
+ # Anthropic Adapter
2
+
3
+ Adapter for Claude models via Anthropic API.
4
+
5
+ ## Class: `RobotLab::Adapters::Anthropic`
6
+
7
+ ```ruby
8
+ # Automatically used for Claude models
9
+ robot = RobotLab.build do
10
+ model "claude-sonnet-4"
11
+ end
12
+ ```
13
+
14
+ ## Supported Models
15
+
16
+ | Model | Description |
17
+ |-------|-------------|
18
+ | `claude-sonnet-4` | Latest Sonnet (recommended) |
19
+ | `claude-opus-4` | Most capable model |
20
+ | `claude-3-5-sonnet-latest` | Claude 3.5 Sonnet |
21
+ | `claude-3-5-haiku-latest` | Fast, efficient model |
22
+
23
+ ## Configuration
24
+
25
+ ### API Key
26
+
27
+ ```bash
28
+ export ANTHROPIC_API_KEY="sk-ant-api03-..."
29
+ ```
30
+
31
+ ### Options
32
+
33
+ ```ruby
34
+ RobotLab.configure do |config|
35
+ config.adapter_options = {
36
+ anthropic: {
37
+ base_url: "https://api.anthropic.com",
38
+ api_version: "2024-01-01",
39
+ timeout: 120,
40
+ max_tokens: 4096
41
+ }
42
+ }
43
+ end
44
+ ```
45
+
46
+ ## Features
47
+
48
+ ### Streaming
49
+
50
+ ```ruby
51
+ result = robot.run(state: state) do |event|
52
+ case event
53
+ when :text_delta
54
+ print event.text
55
+ when :tool_call
56
+ puts "Calling: #{event.name}"
57
+ end
58
+ end
59
+ ```
60
+
61
+ ### Tool Use
62
+
63
+ Tools are automatically converted to Anthropic's format:
64
+
65
+ ```ruby
66
+ robot = RobotLab.build do
67
+ model "claude-sonnet-4"
68
+
69
+ tool :search do
70
+ description "Search the database"
71
+ parameter :query, type: :string, required: true
72
+ handler { |query:, **_| Database.search(query) }
73
+ end
74
+ end
75
+ ```
76
+
77
+ ### Extended Thinking
78
+
79
+ For complex reasoning tasks:
80
+
81
+ ```ruby
82
+ robot = RobotLab.build do
83
+ model "claude-sonnet-4"
84
+ # Extended thinking is automatically enabled for supported models
85
+ end
86
+ ```
87
+
88
+ ## Response Format
89
+
90
+ ```ruby
91
+ {
92
+ content: [TextMessage, ...],
93
+ tool_calls: [ToolCallMessage, ...],
94
+ usage: {
95
+ input_tokens: 150,
96
+ output_tokens: 250,
97
+ cache_creation_input_tokens: 0,
98
+ cache_read_input_tokens: 0
99
+ },
100
+ stop_reason: "end_turn"
101
+ }
102
+ ```
103
+
104
+ ## Error Handling
105
+
106
+ ```ruby
107
+ begin
108
+ result = robot.run(state: state)
109
+ rescue RobotLab::Adapters::RateLimitError => e
110
+ sleep(e.retry_after)
111
+ retry
112
+ rescue RobotLab::Adapters::APIError => e
113
+ logger.error("Anthropic API error: #{e.message}")
114
+ end
115
+ ```
116
+
117
+ ## See Also
118
+
119
+ - [Adapters Overview](index.md)
120
+ - [Streaming Guide](../../guides/streaming.md)
121
+ - [Anthropic API Documentation](https://docs.anthropic.com/)
@@ -0,0 +1,133 @@
1
+ # Gemini Adapter
2
+
3
+ Adapter for Gemini models via Google AI API.
4
+
5
+ ## Class: `RobotLab::Adapters::Gemini`
6
+
7
+ ```ruby
8
+ # Automatically used for Gemini models
9
+ robot = RobotLab.build do
10
+ model "gemini-1.5-pro"
11
+ end
12
+ ```
13
+
14
+ ## Supported Models
15
+
16
+ | Model | Description |
17
+ |-------|-------------|
18
+ | `gemini-1.5-pro` | Most capable Gemini |
19
+ | `gemini-1.5-flash` | Fast, efficient model |
20
+ | `gemini-1.5-flash-8b` | Lightweight model |
21
+ | `gemini-2.0-flash-exp` | Experimental next-gen |
22
+
23
+ ## Configuration
24
+
25
+ ### API Key
26
+
27
+ ```bash
28
+ export GOOGLE_AI_API_KEY="..."
29
+ ```
30
+
31
+ ### Options
32
+
33
+ ```ruby
34
+ RobotLab.configure do |config|
35
+ config.adapter_options = {
36
+ gemini: {
37
+ base_url: "https://generativelanguage.googleapis.com",
38
+ timeout: 120,
39
+ max_tokens: 8192
40
+ }
41
+ }
42
+ end
43
+ ```
44
+
45
+ ### Vertex AI
46
+
47
+ ```ruby
48
+ RobotLab.configure do |config|
49
+ config.adapter_options = {
50
+ gemini: {
51
+ base_url: "https://us-central1-aiplatform.googleapis.com",
52
+ project_id: "your-project",
53
+ location: "us-central1"
54
+ }
55
+ }
56
+ end
57
+ ```
58
+
59
+ ## Features
60
+
61
+ ### Streaming
62
+
63
+ ```ruby
64
+ result = robot.run(state: state) do |event|
65
+ case event
66
+ when :text_delta
67
+ print event.text
68
+ when :tool_call
69
+ puts "Calling: #{event.name}"
70
+ end
71
+ end
72
+ ```
73
+
74
+ ### Tool Use
75
+
76
+ Tools are automatically converted to Gemini's format:
77
+
78
+ ```ruby
79
+ robot = RobotLab.build do
80
+ model "gemini-1.5-pro"
81
+
82
+ tool :search_products do
83
+ description "Search product catalog"
84
+ parameter :query, type: :string, required: true
85
+ parameter :category, type: :string
86
+ handler { |query:, category: nil, **_| Catalog.search(query, category) }
87
+ end
88
+ end
89
+ ```
90
+
91
+ ### Long Context
92
+
93
+ Gemini supports very long contexts:
94
+
95
+ ```ruby
96
+ robot = RobotLab.build do
97
+ model "gemini-1.5-pro"
98
+ # Supports up to 2M tokens context
99
+ end
100
+ ```
101
+
102
+ ## Response Format
103
+
104
+ ```ruby
105
+ {
106
+ content: [TextMessage, ...],
107
+ tool_calls: [ToolCallMessage, ...],
108
+ usage: {
109
+ input_tokens: 150,
110
+ output_tokens: 250
111
+ },
112
+ stop_reason: "STOP"
113
+ }
114
+ ```
115
+
116
+ ## Error Handling
117
+
118
+ ```ruby
119
+ begin
120
+ result = robot.run(state: state)
121
+ rescue RobotLab::Adapters::RateLimitError => e
122
+ sleep(e.retry_after || 60)
123
+ retry
124
+ rescue RobotLab::Adapters::APIError => e
125
+ logger.error("Gemini API error: #{e.message}")
126
+ end
127
+ ```
128
+
129
+ ## See Also
130
+
131
+ - [Adapters Overview](index.md)
132
+ - [Streaming Guide](../../guides/streaming.md)
133
+ - [Google AI Documentation](https://ai.google.dev/docs)