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
@@ -0,0 +1,243 @@
1
+ # Core Concepts
2
+
3
+ This page provides an in-depth look at RobotLab's fundamental building blocks.
4
+
5
+ ## Robot
6
+
7
+ A Robot is the primary unit of computation in RobotLab. It wraps an LLM with:
8
+
9
+ - A unique identity (name, description)
10
+ - A personality (system prompt/template)
11
+ - Capabilities (tools, MCP connections)
12
+ - A specific model configuration
13
+
14
+ ### Robot Anatomy
15
+
16
+ ```ruby
17
+ robot = RobotLab.build do
18
+ name "support_agent" # Unique identifier
19
+ description "Handles support requests" # Used for routing hints
20
+ model "claude-sonnet-4" # LLM model
21
+
22
+ # System prompt - defines personality
23
+ template <<~PROMPT
24
+ You are a friendly customer support agent for Acme Corp.
25
+ Always be polite and helpful. If you don't know something,
26
+ say so honestly.
27
+ PROMPT
28
+
29
+ # Tools - extend capabilities
30
+ tool :lookup_account do
31
+ description "Look up customer account"
32
+ parameter :email, type: :string, required: true
33
+ handler { |email:, **_| Account.find_by_email(email)&.to_h }
34
+ end
35
+
36
+ # MCP - external tool servers
37
+ mcp :inherit # Use network's MCP servers
38
+ end
39
+ ```
40
+
41
+ ### Robot Lifecycle
42
+
43
+ ```mermaid
44
+ stateDiagram-v2
45
+ [*] --> Created: RobotLab.build
46
+ Created --> Running: robot.run(state)
47
+ Running --> ToolExecution: tool_call
48
+ ToolExecution --> Running: result
49
+ Running --> Completed: stop_reason
50
+ Completed --> [*]
51
+ ```
52
+
53
+ ### Robot Properties
54
+
55
+ | Property | Type | Description |
56
+ |----------|------|-------------|
57
+ | `name` | String | Unique identifier within network |
58
+ | `description` | String | What the robot does |
59
+ | `model` | String | LLM model to use |
60
+ | `template` | String | System prompt |
61
+ | `local_tools` | Array | Defined tools |
62
+ | `mcp_clients` | Array | Connected MCP clients |
63
+
64
+ ## Tool
65
+
66
+ Tools give robots the ability to interact with external systems.
67
+
68
+ ### Tool Structure
69
+
70
+ ```ruby
71
+ tool = RobotLab::Tool.new(
72
+ name: "get_weather",
73
+ description: "Get current weather for a location",
74
+ parameters: {
75
+ location: {
76
+ type: "string",
77
+ description: "City name",
78
+ required: true
79
+ },
80
+ unit: {
81
+ type: "string",
82
+ enum: ["celsius", "fahrenheit"],
83
+ default: "celsius"
84
+ }
85
+ },
86
+ handler: ->(location:, unit: "celsius", **_context) {
87
+ WeatherAPI.current(location, unit: unit)
88
+ }
89
+ )
90
+ ```
91
+
92
+ ### Tool Execution
93
+
94
+ When an LLM decides to use a tool:
95
+
96
+ 1. LLM generates a `ToolCallMessage` with tool name and arguments
97
+ 2. RobotLab validates arguments against the tool's schema
98
+ 3. Tool handler is called with validated arguments
99
+ 4. Result is wrapped in a `ToolResultMessage`
100
+ 5. Result is sent back to the LLM for continued processing
101
+
102
+ ### Handler Context
103
+
104
+ Tool handlers receive context about the current execution:
105
+
106
+ ```ruby
107
+ handler: ->(param:, robot:, network:, state:) {
108
+ # robot - The Robot instance executing the tool
109
+ # network - The Network (or NetworkRun) context
110
+ # state - Current State with data, results, memory
111
+ }
112
+ ```
113
+
114
+ !!! tip "Ignoring Context"
115
+ Use `**_context` to accept but ignore context parameters:
116
+ ```ruby
117
+ handler: ->(location:, **_context) { ... }
118
+ ```
119
+
120
+ ## ToolManifest
121
+
122
+ When you need to modify tool metadata without changing functionality:
123
+
124
+ ```ruby
125
+ manifest = RobotLab::ToolManifest.new(
126
+ tool: existing_tool,
127
+ name: "custom_name", # Override name
128
+ description: "New description" # Override description
129
+ )
130
+ ```
131
+
132
+ ## Message Types
133
+
134
+ RobotLab uses a type hierarchy for messages:
135
+
136
+ ```mermaid
137
+ classDiagram
138
+ Message <|-- TextMessage
139
+ Message <|-- ToolMessage
140
+ ToolMessage <|-- ToolCallMessage
141
+ ToolMessage <|-- ToolResultMessage
142
+
143
+ class Message {
144
+ +String type
145
+ +String role
146
+ +String content
147
+ +String stop_reason
148
+ }
149
+
150
+ class TextMessage {
151
+ +text?()
152
+ +user?()
153
+ +assistant?()
154
+ }
155
+
156
+ class ToolMessage {
157
+ +String id
158
+ +String name
159
+ +Hash input
160
+ }
161
+
162
+ class ToolCallMessage {
163
+ +Array~ToolMessage~ tools
164
+ }
165
+
166
+ class ToolResultMessage {
167
+ +ToolMessage tool
168
+ +Hash content
169
+ }
170
+ ```
171
+
172
+ ### Message Roles
173
+
174
+ | Role | Description |
175
+ |------|-------------|
176
+ | `user` | Input from the user |
177
+ | `assistant` | Response from the LLM |
178
+ | `system` | System instructions |
179
+ | `tool` | Tool call or result |
180
+
181
+ ### Stop Reasons
182
+
183
+ | Reason | Description |
184
+ |--------|-------------|
185
+ | `stop` | Natural completion |
186
+ | `tool` | Tool call requested |
187
+ | `max_tokens` | Token limit reached |
188
+
189
+ ## RobotResult
190
+
191
+ The output from a robot execution:
192
+
193
+ ```ruby
194
+ result = robot.run(state: state, network: network)
195
+
196
+ result.robot_name # => "support_agent"
197
+ result.output # => [TextMessage, ...]
198
+ result.tool_calls # => [ToolMessage, ...]
199
+ result.stop_reason # => "stop"
200
+ result.created_at # => Time
201
+ ```
202
+
203
+ ### Accessing Response Content
204
+
205
+ ```ruby
206
+ # Get text response
207
+ text = result.output.select(&:text?).map(&:content).join
208
+
209
+ # Check if tools were called
210
+ has_tools = result.tool_calls.any?
211
+
212
+ # Get all tool names called
213
+ tool_names = result.tool_calls.map(&:name)
214
+ ```
215
+
216
+ ## Configuration Hierarchy
217
+
218
+ RobotLab uses a cascading configuration system:
219
+
220
+ ```
221
+ Global (RobotLab.configure)
222
+
223
+ ├── mcp: [server1, server2]
224
+ ├── tools: [tool1, tool2]
225
+
226
+ └── Network
227
+
228
+ ├── mcp: :inherit | :none | [servers]
229
+ ├── tools: :inherit | :none | [tools]
230
+
231
+ └── Robot
232
+
233
+ ├── mcp: :inherit | :none | [servers]
234
+ └── tools: :inherit | :none | [tools]
235
+ ```
236
+
237
+ The `:inherit` value pulls from the parent level.
238
+
239
+ ## Next Steps
240
+
241
+ - [Robot Execution](robot-execution.md) - Detailed execution flow
242
+ - [Network Orchestration](network-orchestration.md) - Multi-robot coordination
243
+ - [State Management](state-management.md) - Managing conversation state
@@ -0,0 +1,138 @@
1
+ # Architecture Overview
2
+
3
+ RobotLab is designed around a few core architectural principles that enable flexible, composable AI workflows.
4
+
5
+ ## Design Philosophy
6
+
7
+ ### 1. Separation of Concerns
8
+
9
+ Each component has a single, well-defined responsibility:
10
+
11
+ - **Robot**: Encapsulates LLM interaction logic and personality
12
+ - **Network**: Orchestrates robot execution and routing
13
+ - **State**: Manages conversation and workflow data
14
+ - **Tool**: Provides external capabilities to robots
15
+
16
+ ### 2. Composability
17
+
18
+ Components are designed to be mixed and matched:
19
+
20
+ - Robots can be reused across multiple networks
21
+ - Tools can be shared or robot-specific
22
+ - Networks can be nested or chained
23
+ - State can be persisted and restored
24
+
25
+ ### 3. Provider Agnostic
26
+
27
+ RobotLab abstracts away LLM provider differences:
28
+
29
+ - Unified message format across providers
30
+ - Consistent tool calling interface
31
+ - Automatic provider detection from model names
32
+ - Easy switching between providers
33
+
34
+ ## System Architecture
35
+
36
+ ```mermaid
37
+ graph TB
38
+ subgraph "Application Layer"
39
+ A[Your Application]
40
+ end
41
+
42
+ subgraph "RobotLab Core"
43
+ B[Network]
44
+ C[Router]
45
+ D[Robot]
46
+ E[State]
47
+ F[Memory]
48
+ end
49
+
50
+ subgraph "Integration Layer"
51
+ G[Adapters]
52
+ H[MCP Client]
53
+ I[Tools]
54
+ end
55
+
56
+ subgraph "Provider Layer"
57
+ J[Anthropic]
58
+ K[OpenAI]
59
+ L[Gemini]
60
+ M[MCP Servers]
61
+ end
62
+
63
+ A --> B
64
+ B --> C
65
+ B --> D
66
+ B --> E
67
+ E --> F
68
+ D --> G
69
+ D --> H
70
+ D --> I
71
+ G --> J
72
+ G --> K
73
+ G --> L
74
+ H --> M
75
+ ```
76
+
77
+ ## Core Components
78
+
79
+ | Component | Description | Documentation |
80
+ |-----------|-------------|---------------|
81
+ | **Robot** | LLM-powered agent with personality and tools | [Core Concepts](core-concepts.md) |
82
+ | **Network** | Orchestrates multiple robots | [Network Orchestration](network-orchestration.md) |
83
+ | **State** | Conversation and workflow data | [State Management](state-management.md) |
84
+ | **Router** | Determines robot execution order | [Network Orchestration](network-orchestration.md) |
85
+ | **Memory** | Shared key-value store | [State Management](state-management.md) |
86
+ | **Adapter** | Provider-specific message conversion | [Message Flow](message-flow.md) |
87
+
88
+ ## Data Flow
89
+
90
+ 1. **Input**: User message enters via State
91
+ 2. **Routing**: Router selects robot(s) to execute
92
+ 3. **Execution**: Robot processes message with LLM
93
+ 4. **Tools**: Robot may call tools during execution
94
+ 5. **Result**: Robot returns RobotResult
95
+ 6. **Iteration**: Router checks for next robot
96
+ 7. **Output**: Final results returned to application
97
+
98
+ ## Key Patterns
99
+
100
+ ### Builder Pattern
101
+
102
+ Robots and networks are constructed using a fluent DSL:
103
+
104
+ ```ruby
105
+ robot = RobotLab.build do
106
+ name "assistant"
107
+ model "claude-sonnet-4"
108
+ template "You are helpful."
109
+ end
110
+ ```
111
+
112
+ ### Strategy Pattern
113
+
114
+ Routers implement custom selection logic:
115
+
116
+ ```ruby
117
+ router = ->(args) {
118
+ # Custom routing strategy
119
+ args.call_count.zero? ? :first_robot : nil
120
+ }
121
+ ```
122
+
123
+ ### Adapter Pattern
124
+
125
+ Provider adapters normalize LLM interfaces:
126
+
127
+ ```ruby
128
+ adapter = Adapters::Registry.for(:anthropic)
129
+ messages = adapter.format_messages(robot_lab_messages)
130
+ ```
131
+
132
+ ## Next Steps
133
+
134
+ - [Core Concepts](core-concepts.md) - Deep dive into robots and tools
135
+ - [Robot Execution](robot-execution.md) - How robots process messages
136
+ - [Network Orchestration](network-orchestration.md) - Multi-robot workflows
137
+ - [State Management](state-management.md) - Managing conversation state
138
+ - [Message Flow](message-flow.md) - How messages move through the system
@@ -0,0 +1,320 @@
1
+ # Message Flow
2
+
3
+ This page explains how messages move through RobotLab, from user input to LLM response.
4
+
5
+ ## Message Types
6
+
7
+ RobotLab uses four primary message types:
8
+
9
+ ```mermaid
10
+ classDiagram
11
+ class Message {
12
+ <<abstract>>
13
+ +type: String
14
+ +role: String
15
+ +content: String
16
+ +stop_reason: String
17
+ }
18
+
19
+ class TextMessage {
20
+ +text?() bool
21
+ +user?() bool
22
+ +assistant?() bool
23
+ +system?() bool
24
+ }
25
+
26
+ class ToolMessage {
27
+ +id: String
28
+ +name: String
29
+ +input: Hash
30
+ +tool_call?() bool
31
+ }
32
+
33
+ class ToolCallMessage {
34
+ +tools: Array~ToolMessage~
35
+ }
36
+
37
+ class ToolResultMessage {
38
+ +tool: ToolMessage
39
+ +content: Hash
40
+ +tool_result?() bool
41
+ }
42
+
43
+ Message <|-- TextMessage
44
+ Message <|-- ToolMessage
45
+ ToolMessage <|-- ToolCallMessage
46
+ ToolMessage <|-- ToolResultMessage
47
+ ```
48
+
49
+ ### TextMessage
50
+
51
+ Regular text content from users or assistants:
52
+
53
+ ```ruby
54
+ TextMessage.new(
55
+ role: "user",
56
+ content: "What's the weather in Paris?"
57
+ )
58
+
59
+ TextMessage.new(
60
+ role: "assistant",
61
+ content: "The weather in Paris is sunny and 22°C.",
62
+ stop_reason: "stop"
63
+ )
64
+ ```
65
+
66
+ ### ToolMessage
67
+
68
+ Base class for tool-related messages:
69
+
70
+ ```ruby
71
+ ToolMessage.new(
72
+ id: "tool_123",
73
+ name: "get_weather",
74
+ input: { location: "Paris" }
75
+ )
76
+ ```
77
+
78
+ ### ToolCallMessage
79
+
80
+ LLM's request to execute tools:
81
+
82
+ ```ruby
83
+ ToolCallMessage.new(
84
+ role: "assistant",
85
+ content: nil,
86
+ stop_reason: "tool",
87
+ tools: [
88
+ ToolMessage.new(id: "call_1", name: "get_weather", input: { location: "Paris" })
89
+ ]
90
+ )
91
+ ```
92
+
93
+ ### ToolResultMessage
94
+
95
+ Result from tool execution:
96
+
97
+ ```ruby
98
+ ToolResultMessage.new(
99
+ tool: tool_message,
100
+ content: { data: { temp: 22, condition: "sunny" } }
101
+ )
102
+ ```
103
+
104
+ ## Message Flow Diagram
105
+
106
+ ```mermaid
107
+ sequenceDiagram
108
+ participant User
109
+ participant State
110
+ participant Robot
111
+ participant Adapter
112
+ participant LLM
113
+
114
+ User->>State: UserMessage
115
+ State->>State: Format messages
116
+ State->>Robot: state.messages
117
+ Robot->>Adapter: Convert messages
118
+ Adapter->>LLM: Provider-specific format
119
+
120
+ LLM-->>Adapter: Response
121
+ Adapter-->>Robot: Parse response
122
+ Robot->>Robot: Execute tools (if any)
123
+ Robot-->>State: RobotResult
124
+ State->>State: Append result
125
+ State-->>User: Response
126
+ ```
127
+
128
+ ## Adapter Layer
129
+
130
+ Adapters convert between RobotLab's message format and provider-specific formats.
131
+
132
+ ### Anthropic Adapter
133
+
134
+ ```ruby
135
+ # RobotLab format
136
+ messages = [
137
+ TextMessage.new(role: "user", content: "Hello"),
138
+ TextMessage.new(role: "assistant", content: "Hi there!")
139
+ ]
140
+
141
+ # Converted to Anthropic format
142
+ [
143
+ { role: "user", content: "Hello" },
144
+ { role: "assistant", content: "Hi there!" }
145
+ ]
146
+ ```
147
+
148
+ ### System Message Handling
149
+
150
+ System messages are handled specially:
151
+
152
+ ```ruby
153
+ # RobotLab
154
+ messages = [
155
+ TextMessage.new(role: "system", content: "You are helpful."),
156
+ TextMessage.new(role: "user", content: "Hello")
157
+ ]
158
+
159
+ # Anthropic: system extracted separately
160
+ # OpenAI: system message stays in array
161
+ # Gemini: converted to context
162
+ ```
163
+
164
+ ### Tool Message Conversion
165
+
166
+ Tool calls are provider-specific:
167
+
168
+ === "Anthropic"
169
+
170
+ ```ruby
171
+ {
172
+ type: "tool_use",
173
+ id: "tool_123",
174
+ name: "get_weather",
175
+ input: { location: "Paris" }
176
+ }
177
+ ```
178
+
179
+ === "OpenAI"
180
+
181
+ ```ruby
182
+ {
183
+ type: "function",
184
+ function: {
185
+ name: "get_weather",
186
+ arguments: '{"location":"Paris"}'
187
+ }
188
+ }
189
+ ```
190
+
191
+ ## Conversation Building
192
+
193
+ ### Initial State
194
+
195
+ ```ruby
196
+ state = RobotLab.create_state(
197
+ message: "What's the weather?",
198
+ data: { location: "Paris" }
199
+ )
200
+
201
+ state.messages
202
+ # => [TextMessage(role: "user", content: "What's the weather?")]
203
+ ```
204
+
205
+ ### After Robot Execution
206
+
207
+ ```ruby
208
+ result = robot.run(state: state, network: network)
209
+ state.append_result(result)
210
+
211
+ state.messages
212
+ # => [
213
+ # TextMessage(role: "user", content: "What's the weather?"),
214
+ # TextMessage(role: "assistant", content: "The weather is sunny.")
215
+ # ]
216
+ ```
217
+
218
+ ### With Tool Calls
219
+
220
+ ```ruby
221
+ state.messages
222
+ # => [
223
+ # TextMessage(role: "user", content: "What's the weather?"),
224
+ # ToolCallMessage(tools: [ToolMessage(name: "get_weather", ...)]),
225
+ # ToolResultMessage(content: { temp: 22 }),
226
+ # TextMessage(role: "assistant", content: "It's 22°C and sunny.")
227
+ # ]
228
+ ```
229
+
230
+ ## Format History
231
+
232
+ The `format_history` method prepares messages for LLM:
233
+
234
+ ```ruby
235
+ # Includes results from previous robots
236
+ formatted = state.format_history
237
+
238
+ # Returns alternating user/assistant messages
239
+ # with tool calls/results properly interleaved
240
+ ```
241
+
242
+ ## Message Predicates
243
+
244
+ Check message types easily:
245
+
246
+ ```ruby
247
+ message.text? # Is it a TextMessage?
248
+ message.tool_call? # Is it a ToolCallMessage?
249
+ message.tool_result? # Is it a ToolResultMessage?
250
+
251
+ message.user? # Is role "user"?
252
+ message.assistant? # Is role "assistant"?
253
+ message.system? # Is role "system"?
254
+
255
+ message.stopped? # Is stop_reason "stop"?
256
+ message.tool_stop? # Is stop_reason "tool"?
257
+ ```
258
+
259
+ ## Creating Messages
260
+
261
+ ### From Strings
262
+
263
+ ```ruby
264
+ TextMessage.new(role: "user", content: "Hello")
265
+ ```
266
+
267
+ ### From Hashes
268
+
269
+ ```ruby
270
+ Message.from_hash(
271
+ type: "text",
272
+ role: "user",
273
+ content: "Hello"
274
+ )
275
+ ```
276
+
277
+ ### From UserMessage
278
+
279
+ ```ruby
280
+ user_msg = UserMessage.new("Hello", thread_id: "123")
281
+ text_msg = user_msg.to_message
282
+ # => TextMessage(role: "user", content: "Hello")
283
+ ```
284
+
285
+ ## Serialization
286
+
287
+ Messages can be serialized:
288
+
289
+ ```ruby
290
+ # To hash
291
+ hash = message.to_h
292
+ # => { type: "text", role: "user", content: "Hello" }
293
+
294
+ # To JSON
295
+ json = message.to_json
296
+ # => '{"type":"text","role":"user","content":"Hello"}'
297
+
298
+ # From hash
299
+ message = Message.from_hash(hash)
300
+ ```
301
+
302
+ ## Provider Registry
303
+
304
+ The adapter registry maps providers to adapters:
305
+
306
+ ```ruby
307
+ Adapters::Registry.for(:anthropic) # => Adapters::Anthropic
308
+ Adapters::Registry.for(:openai) # => Adapters::OpenAI
309
+ Adapters::Registry.for(:gemini) # => Adapters::Gemini
310
+
311
+ # Aliases
312
+ Adapters::Registry.for(:azure_openai) # => Adapters::OpenAI
313
+ Adapters::Registry.for(:bedrock) # => Adapters::Anthropic
314
+ ```
315
+
316
+ ## Next Steps
317
+
318
+ - [Building Robots](../guides/building-robots.md) - Creating custom robots
319
+ - [Using Tools](../guides/using-tools.md) - Tool message handling
320
+ - [API Reference: Messages](../api/messages/index.md) - Detailed message API