robot_lab 0.0.4 → 0.0.6
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 +50 -0
- data/README.md +64 -6
- data/Rakefile +2 -1
- data/docs/api/core/index.md +41 -46
- data/docs/api/core/memory.md +200 -154
- data/docs/api/core/network.md +13 -3
- data/docs/api/core/robot.md +38 -26
- data/docs/api/core/state.md +55 -73
- data/docs/api/index.md +7 -28
- data/docs/api/messages/index.md +35 -20
- data/docs/api/messages/text-message.md +67 -21
- data/docs/api/messages/tool-call-message.md +80 -41
- data/docs/api/messages/tool-result-message.md +119 -50
- data/docs/api/messages/user-message.md +48 -24
- data/docs/architecture/core-concepts.md +10 -15
- data/docs/concepts.md +5 -7
- data/docs/examples/index.md +2 -2
- data/docs/getting-started/configuration.md +80 -0
- data/docs/guides/building-robots.md +10 -9
- data/docs/guides/creating-networks.md +49 -0
- data/docs/guides/index.md +0 -5
- data/docs/guides/rails-integration.md +244 -162
- data/docs/guides/streaming.md +118 -138
- data/docs/index.md +0 -8
- data/examples/03_network.rb +10 -7
- data/examples/08_llm_config.rb +40 -11
- data/examples/09_chaining.rb +45 -6
- data/examples/11_network_introspection.rb +30 -7
- data/examples/12_message_bus.rb +1 -1
- data/examples/14_rusty_circuit/heckler.rb +14 -8
- data/examples/14_rusty_circuit/open_mic.rb +5 -3
- data/examples/14_rusty_circuit/scout.rb +14 -31
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +1 -1
- data/examples/16_writers_room/display.rb +158 -0
- data/examples/16_writers_room/output/.gitignore +2 -0
- data/examples/16_writers_room/output/opus_001.md +263 -0
- data/examples/16_writers_room/output/opus_001_notes.log +470 -0
- data/examples/16_writers_room/prompts/writer.md +37 -0
- data/examples/16_writers_room/room.rb +150 -0
- data/examples/16_writers_room/tools.rb +162 -0
- data/examples/16_writers_room/writer.rb +121 -0
- data/examples/16_writers_room/writers_room.rb +162 -0
- data/lib/generators/robot_lab/templates/initializer.rb.tt +0 -13
- data/lib/robot_lab/memory.rb +8 -32
- data/lib/robot_lab/network.rb +13 -20
- data/lib/robot_lab/robot/bus_messaging.rb +239 -0
- data/lib/robot_lab/robot/mcp_management.rb +88 -0
- data/lib/robot_lab/robot/template_rendering.rb +130 -0
- data/lib/robot_lab/robot.rb +56 -420
- data/lib/robot_lab/run_config.rb +184 -0
- data/lib/robot_lab/state_proxy.rb +2 -12
- data/lib/robot_lab/task.rb +8 -1
- data/lib/robot_lab/utils.rb +39 -0
- data/lib/robot_lab/version.rb +1 -1
- data/lib/robot_lab.rb +29 -8
- data/mkdocs.yml +0 -11
- metadata +15 -20
- data/docs/api/adapters/anthropic.md +0 -121
- data/docs/api/adapters/gemini.md +0 -133
- data/docs/api/adapters/index.md +0 -104
- data/docs/api/adapters/openai.md +0 -134
- data/docs/api/history/active-record-adapter.md +0 -275
- data/docs/api/history/config.md +0 -284
- data/docs/api/history/index.md +0 -128
- data/docs/api/history/thread-manager.md +0 -194
- data/docs/guides/history.md +0 -359
- data/lib/robot_lab/adapters/anthropic.rb +0 -163
- data/lib/robot_lab/adapters/base.rb +0 -85
- data/lib/robot_lab/adapters/gemini.rb +0 -193
- data/lib/robot_lab/adapters/openai.rb +0 -160
- data/lib/robot_lab/adapters/registry.rb +0 -81
- data/lib/robot_lab/errors.rb +0 -70
- data/lib/robot_lab/history/active_record_adapter.rb +0 -146
- data/lib/robot_lab/history/config.rb +0 -115
- data/lib/robot_lab/history/thread_manager.rb +0 -93
- data/lib/robot_lab/robotic_model.rb +0 -324
data/docs/api/adapters/gemini.md
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
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)
|
data/docs/api/adapters/index.md
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
# Adapters
|
|
2
|
-
|
|
3
|
-
LLM provider adapters for unified API access.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
Adapters provide a consistent interface to different LLM providers, handling the translation between RobotLab's message format and provider-specific APIs.
|
|
8
|
-
|
|
9
|
-
```ruby
|
|
10
|
-
# Configure globally
|
|
11
|
-
RobotLab.configure do |config|
|
|
12
|
-
config.default_model = "claude-sonnet-4"
|
|
13
|
-
# Adapter is selected automatically based on model
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Or configure per-robot
|
|
17
|
-
robot = RobotLab.build do
|
|
18
|
-
model "gpt-4o" # Uses OpenAI adapter
|
|
19
|
-
end
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
## Adapter Selection
|
|
23
|
-
|
|
24
|
-
Adapters are automatically selected based on model name:
|
|
25
|
-
|
|
26
|
-
| Model Pattern | Adapter |
|
|
27
|
-
|---------------|---------|
|
|
28
|
-
| `claude-*`, `anthropic/*` | Anthropic |
|
|
29
|
-
| `gpt-*`, `o1-*`, `openai/*` | OpenAI |
|
|
30
|
-
| `gemini-*`, `google/*` | Gemini |
|
|
31
|
-
|
|
32
|
-
## Common Interface
|
|
33
|
-
|
|
34
|
-
All adapters implement:
|
|
35
|
-
|
|
36
|
-
```ruby
|
|
37
|
-
adapter.chat(
|
|
38
|
-
messages: messages,
|
|
39
|
-
model: model,
|
|
40
|
-
tools: tools,
|
|
41
|
-
system: system_prompt,
|
|
42
|
-
streaming: callback
|
|
43
|
-
)
|
|
44
|
-
# => Response with content and usage
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Available Adapters
|
|
48
|
-
|
|
49
|
-
| Adapter | Description |
|
|
50
|
-
|---------|-------------|
|
|
51
|
-
| [Anthropic](anthropic.md) | Claude models via Anthropic API |
|
|
52
|
-
| [OpenAI](openai.md) | GPT models via OpenAI API |
|
|
53
|
-
| [Gemini](gemini.md) | Gemini models via Google AI |
|
|
54
|
-
|
|
55
|
-
## Configuration
|
|
56
|
-
|
|
57
|
-
### API Keys
|
|
58
|
-
|
|
59
|
-
Set via environment variables:
|
|
60
|
-
|
|
61
|
-
```bash
|
|
62
|
-
export ANTHROPIC_API_KEY="sk-ant-..."
|
|
63
|
-
export OPENAI_API_KEY="sk-..."
|
|
64
|
-
export GOOGLE_AI_API_KEY="..."
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### Custom Endpoints
|
|
68
|
-
|
|
69
|
-
```ruby
|
|
70
|
-
RobotLab.configure do |config|
|
|
71
|
-
config.adapter_options = {
|
|
72
|
-
anthropic: { base_url: "https://custom.anthropic.endpoint" },
|
|
73
|
-
openai: { base_url: "https://custom.openai.endpoint" }
|
|
74
|
-
}
|
|
75
|
-
end
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## Creating Custom Adapters
|
|
79
|
-
|
|
80
|
-
Implement the adapter interface:
|
|
81
|
-
|
|
82
|
-
```ruby
|
|
83
|
-
class MyAdapter
|
|
84
|
-
def chat(messages:, model:, tools: [], system: nil, streaming: nil)
|
|
85
|
-
# Translate messages to provider format
|
|
86
|
-
# Make API call
|
|
87
|
-
# Translate response back
|
|
88
|
-
|
|
89
|
-
Response.new(
|
|
90
|
-
content: content,
|
|
91
|
-
tool_calls: tool_calls,
|
|
92
|
-
usage: { input_tokens: x, output_tokens: y }
|
|
93
|
-
)
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Register the adapter
|
|
98
|
-
RobotLab.register_adapter(:my_provider, MyAdapter)
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## See Also
|
|
102
|
-
|
|
103
|
-
- [Configuration Guide](../../getting-started/configuration.md)
|
|
104
|
-
- [Streaming Guide](../../guides/streaming.md)
|
data/docs/api/adapters/openai.md
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
# OpenAI Adapter
|
|
2
|
-
|
|
3
|
-
Adapter for GPT models via OpenAI API.
|
|
4
|
-
|
|
5
|
-
## Class: `RobotLab::Adapters::OpenAI`
|
|
6
|
-
|
|
7
|
-
```ruby
|
|
8
|
-
# Automatically used for GPT models
|
|
9
|
-
robot = RobotLab.build do
|
|
10
|
-
model "gpt-4o"
|
|
11
|
-
end
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## Supported Models
|
|
15
|
-
|
|
16
|
-
| Model | Description |
|
|
17
|
-
|-------|-------------|
|
|
18
|
-
| `gpt-4o` | Latest GPT-4 Omni |
|
|
19
|
-
| `gpt-4o-mini` | Fast, efficient GPT-4 |
|
|
20
|
-
| `gpt-4-turbo` | GPT-4 Turbo |
|
|
21
|
-
| `o1-preview` | Reasoning model |
|
|
22
|
-
| `o1-mini` | Fast reasoning model |
|
|
23
|
-
|
|
24
|
-
## Configuration
|
|
25
|
-
|
|
26
|
-
### API Key
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
export OPENAI_API_KEY="sk-..."
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### Options
|
|
33
|
-
|
|
34
|
-
```ruby
|
|
35
|
-
RobotLab.configure do |config|
|
|
36
|
-
config.adapter_options = {
|
|
37
|
-
openai: {
|
|
38
|
-
base_url: "https://api.openai.com/v1",
|
|
39
|
-
organization: "org-...",
|
|
40
|
-
timeout: 120,
|
|
41
|
-
max_tokens: 4096
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
end
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### Azure OpenAI
|
|
48
|
-
|
|
49
|
-
```ruby
|
|
50
|
-
RobotLab.configure do |config|
|
|
51
|
-
config.adapter_options = {
|
|
52
|
-
openai: {
|
|
53
|
-
base_url: "https://your-resource.openai.azure.com",
|
|
54
|
-
api_key: ENV["AZURE_OPENAI_KEY"],
|
|
55
|
-
api_version: "2024-02-15-preview"
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
end
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Features
|
|
62
|
-
|
|
63
|
-
### Streaming
|
|
64
|
-
|
|
65
|
-
```ruby
|
|
66
|
-
result = robot.run(state: state) do |event|
|
|
67
|
-
case event
|
|
68
|
-
when :text_delta
|
|
69
|
-
print event.text
|
|
70
|
-
when :tool_call
|
|
71
|
-
puts "Calling: #{event.name}"
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### Tool Use
|
|
77
|
-
|
|
78
|
-
Tools are automatically converted to OpenAI's function calling format:
|
|
79
|
-
|
|
80
|
-
```ruby
|
|
81
|
-
robot = RobotLab.build do
|
|
82
|
-
model "gpt-4o"
|
|
83
|
-
|
|
84
|
-
tool :get_weather do
|
|
85
|
-
description "Get current weather"
|
|
86
|
-
parameter :location, type: :string, required: true
|
|
87
|
-
handler { |location:, **_| WeatherAPI.fetch(location) }
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### JSON Mode
|
|
93
|
-
|
|
94
|
-
```ruby
|
|
95
|
-
robot = RobotLab.build do
|
|
96
|
-
model "gpt-4o"
|
|
97
|
-
template "Always respond with valid JSON."
|
|
98
|
-
# Response format is automatically configured
|
|
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
|
-
total_tokens: 400
|
|
112
|
-
},
|
|
113
|
-
stop_reason: "stop"
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## Error Handling
|
|
118
|
-
|
|
119
|
-
```ruby
|
|
120
|
-
begin
|
|
121
|
-
result = robot.run(state: state)
|
|
122
|
-
rescue RobotLab::Adapters::RateLimitError => e
|
|
123
|
-
sleep(e.retry_after || 60)
|
|
124
|
-
retry
|
|
125
|
-
rescue RobotLab::Adapters::APIError => e
|
|
126
|
-
logger.error("OpenAI API error: #{e.message}")
|
|
127
|
-
end
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
## See Also
|
|
131
|
-
|
|
132
|
-
- [Adapters Overview](index.md)
|
|
133
|
-
- [Streaming Guide](../../guides/streaming.md)
|
|
134
|
-
- [OpenAI API Documentation](https://platform.openai.com/docs/)
|
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
# History::ActiveRecordAdapter
|
|
2
|
-
|
|
3
|
-
ActiveRecord-based history persistence adapter for Rails applications.
|
|
4
|
-
|
|
5
|
-
## Class: `RobotLab::History::ActiveRecordAdapter`
|
|
6
|
-
|
|
7
|
-
Provides thread and result storage using ActiveRecord models. Converts itself to a `History::Config` via `to_config` for use with networks and thread managers.
|
|
8
|
-
|
|
9
|
-
```ruby
|
|
10
|
-
adapter = RobotLab::History::ActiveRecordAdapter.new(
|
|
11
|
-
thread_model: RobotLabThread,
|
|
12
|
-
result_model: RobotLabResult
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
config = adapter.to_config
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Constructor
|
|
19
|
-
|
|
20
|
-
```ruby
|
|
21
|
-
ActiveRecordAdapter.new(thread_model:, result_model:)
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
**Parameters:**
|
|
25
|
-
|
|
26
|
-
| Name | Type | Description |
|
|
27
|
-
|------|------|-------------|
|
|
28
|
-
| `thread_model` | `Class` | ActiveRecord model class for conversation threads |
|
|
29
|
-
| `result_model` | `Class` | ActiveRecord model class for conversation results |
|
|
30
|
-
|
|
31
|
-
## Attributes
|
|
32
|
-
|
|
33
|
-
### thread_model
|
|
34
|
-
|
|
35
|
-
```ruby
|
|
36
|
-
adapter.thread_model # => Class (ActiveRecord model)
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
The ActiveRecord model class used for storing conversation threads.
|
|
40
|
-
|
|
41
|
-
### result_model
|
|
42
|
-
|
|
43
|
-
```ruby
|
|
44
|
-
adapter.result_model # => Class (ActiveRecord model)
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
The ActiveRecord model class used for storing conversation results.
|
|
48
|
-
|
|
49
|
-
## Methods
|
|
50
|
-
|
|
51
|
-
### to_config
|
|
52
|
-
|
|
53
|
-
```ruby
|
|
54
|
-
config = adapter.to_config # => RobotLab::History::Config
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Convert the adapter to a `History::Config` object. The config's callbacks delegate to the adapter's `create_thread`, `get`, `append_user_message`, and `append_results` methods.
|
|
58
|
-
|
|
59
|
-
### create_thread
|
|
60
|
-
|
|
61
|
-
```ruby
|
|
62
|
-
adapter.create_thread(state:, input:, **)
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
Create a new conversation thread record. Generates a UUID `session_id`, extracts content and metadata from the input, and stores `state.data` as serialized state data.
|
|
66
|
-
|
|
67
|
-
**Parameters:**
|
|
68
|
-
|
|
69
|
-
| Name | Type | Description |
|
|
70
|
-
|------|------|-------------|
|
|
71
|
-
| `state` | `Object` | Current memory/state (must respond to `.data`) |
|
|
72
|
-
| `input` | `String`, `UserMessage` | Initial user input |
|
|
73
|
-
|
|
74
|
-
**Returns:** Hash with `{ session_id: "...", created_at: Time }`.
|
|
75
|
-
|
|
76
|
-
### get
|
|
77
|
-
|
|
78
|
-
```ruby
|
|
79
|
-
results = adapter.get(session_id:, **)
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Retrieve all results for a thread, ordered by `sequence_number` and `created_at`. Deserializes each record into a `RobotResult`.
|
|
83
|
-
|
|
84
|
-
**Parameters:**
|
|
85
|
-
|
|
86
|
-
| Name | Type | Description |
|
|
87
|
-
|------|------|-------------|
|
|
88
|
-
| `session_id` | `String` | Thread identifier |
|
|
89
|
-
|
|
90
|
-
**Returns:** `Array<RobotResult>` -- deserialized results.
|
|
91
|
-
|
|
92
|
-
### append_user_message
|
|
93
|
-
|
|
94
|
-
```ruby
|
|
95
|
-
adapter.append_user_message(session_id:, message:, **)
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Update the thread record with the latest user message content and timestamp.
|
|
99
|
-
|
|
100
|
-
**Parameters:**
|
|
101
|
-
|
|
102
|
-
| Name | Type | Description |
|
|
103
|
-
|------|------|-------------|
|
|
104
|
-
| `session_id` | `String` | Thread identifier |
|
|
105
|
-
| `message` | `UserMessage` | User message |
|
|
106
|
-
|
|
107
|
-
### append_results
|
|
108
|
-
|
|
109
|
-
```ruby
|
|
110
|
-
adapter.append_results(session_id:, new_results:, **)
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
Append robot results to the thread. Each result is stored with an auto-incrementing `sequence_number`. Serializes `output` and `tool_calls` from each `RobotResult`. Also updates the thread's `updated_at` timestamp.
|
|
114
|
-
|
|
115
|
-
**Parameters:**
|
|
116
|
-
|
|
117
|
-
| Name | Type | Description |
|
|
118
|
-
|------|------|-------------|
|
|
119
|
-
| `session_id` | `String` | Thread identifier |
|
|
120
|
-
| `new_results` | `Array<RobotResult>` | Results to append |
|
|
121
|
-
|
|
122
|
-
## Model Requirements
|
|
123
|
-
|
|
124
|
-
### Thread Model
|
|
125
|
-
|
|
126
|
-
The thread model must have the following columns:
|
|
127
|
-
|
|
128
|
-
```ruby
|
|
129
|
-
# db/migrate/xxx_create_robot_lab_threads.rb
|
|
130
|
-
create_table :robot_lab_threads do |t|
|
|
131
|
-
t.string :session_id, null: false, index: { unique: true }
|
|
132
|
-
t.text :initial_input
|
|
133
|
-
t.jsonb :input_metadata, default: {}
|
|
134
|
-
t.jsonb :state_data, default: {}
|
|
135
|
-
t.text :last_user_message
|
|
136
|
-
t.datetime :last_user_message_at
|
|
137
|
-
t.timestamps
|
|
138
|
-
end
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Result Model
|
|
142
|
-
|
|
143
|
-
The result model must have the following columns:
|
|
144
|
-
|
|
145
|
-
```ruby
|
|
146
|
-
# db/migrate/xxx_create_robot_lab_results.rb
|
|
147
|
-
create_table :robot_lab_results do |t|
|
|
148
|
-
t.string :session_id, null: false, index: true
|
|
149
|
-
t.string :robot_name
|
|
150
|
-
t.integer :sequence_number
|
|
151
|
-
t.jsonb :output_data, default: []
|
|
152
|
-
t.jsonb :tool_calls_data, default: []
|
|
153
|
-
t.string :stop_reason
|
|
154
|
-
t.string :checksum
|
|
155
|
-
t.timestamps
|
|
156
|
-
end
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
## Examples
|
|
160
|
-
|
|
161
|
-
### Basic Setup
|
|
162
|
-
|
|
163
|
-
```ruby
|
|
164
|
-
adapter = RobotLab::History::ActiveRecordAdapter.new(
|
|
165
|
-
thread_model: RobotLabThread,
|
|
166
|
-
result_model: RobotLabResult
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
config = adapter.to_config
|
|
170
|
-
# Use config with a ThreadManager or pass to network configuration
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### Using with ThreadManager
|
|
174
|
-
|
|
175
|
-
```ruby
|
|
176
|
-
adapter = RobotLab::History::ActiveRecordAdapter.new(
|
|
177
|
-
thread_model: RobotLabThread,
|
|
178
|
-
result_model: RobotLabResult
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
manager = RobotLab::History::ThreadManager.new(adapter.to_config)
|
|
182
|
-
|
|
183
|
-
# Create a thread
|
|
184
|
-
session_id = manager.create_thread(state: memory, input: "Hello")
|
|
185
|
-
|
|
186
|
-
# Run robot and save
|
|
187
|
-
result = robot.run("Hello")
|
|
188
|
-
manager.append_results(session_id: session_id, results: [result])
|
|
189
|
-
|
|
190
|
-
# Later, retrieve
|
|
191
|
-
history = manager.get_history(session_id)
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### With User-Scoped Models
|
|
195
|
-
|
|
196
|
-
For applications that need per-user thread scoping, create a custom adapter:
|
|
197
|
-
|
|
198
|
-
```ruby
|
|
199
|
-
class ScopedHistoryAdapter
|
|
200
|
-
def initialize(thread_model:, result_model:)
|
|
201
|
-
@thread_model = thread_model
|
|
202
|
-
@result_model = result_model
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
def to_config
|
|
206
|
-
RobotLab::History::Config.new(
|
|
207
|
-
create_thread: method(:create_thread),
|
|
208
|
-
get: method(:get),
|
|
209
|
-
append_user_message: method(:append_user_message),
|
|
210
|
-
append_results: method(:append_results)
|
|
211
|
-
)
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
private
|
|
215
|
-
|
|
216
|
-
def create_thread(state:, input:, user_id:, **)
|
|
217
|
-
thread = @thread_model.create!(
|
|
218
|
-
session_id: SecureRandom.uuid,
|
|
219
|
-
user_id: user_id,
|
|
220
|
-
initial_input: input.to_s
|
|
221
|
-
)
|
|
222
|
-
{ session_id: thread.session_id, created_at: thread.created_at }
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
def get(session_id:, user_id:, **)
|
|
226
|
-
@result_model
|
|
227
|
-
.joins(:thread)
|
|
228
|
-
.where(threads: { session_id: session_id, user_id: user_id })
|
|
229
|
-
.order(:sequence_number)
|
|
230
|
-
.map(&:to_robot_result)
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
def append_user_message(session_id:, message:, user_id:, **)
|
|
234
|
-
@thread_model.where(session_id: session_id, user_id: user_id)
|
|
235
|
-
.update_all(last_user_message: message.content, last_user_message_at: Time.current)
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
def append_results(session_id:, new_results:, user_id:, **)
|
|
239
|
-
base_seq = @result_model.where(session_id: session_id).maximum(:sequence_number) || 0
|
|
240
|
-
|
|
241
|
-
new_results.each_with_index do |result, i|
|
|
242
|
-
@result_model.create!(
|
|
243
|
-
session_id: session_id,
|
|
244
|
-
robot_name: result.robot_name,
|
|
245
|
-
sequence_number: base_seq + i + 1,
|
|
246
|
-
output_data: result.output.map(&:to_h),
|
|
247
|
-
tool_calls_data: result.tool_calls.map(&:to_h),
|
|
248
|
-
stop_reason: result.stop_reason,
|
|
249
|
-
checksum: result.checksum
|
|
250
|
-
)
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
@thread_model.where(session_id: session_id).update_all(updated_at: Time.current)
|
|
254
|
-
end
|
|
255
|
-
end
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
### Rails Generator
|
|
259
|
-
|
|
260
|
-
Use the Rails generator to scaffold the required models and migrations:
|
|
261
|
-
|
|
262
|
-
```bash
|
|
263
|
-
rails generate robot_lab:history
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
This creates:
|
|
267
|
-
- Thread and result ActiveRecord models
|
|
268
|
-
- Database migrations with required columns
|
|
269
|
-
- Initializer configuration
|
|
270
|
-
|
|
271
|
-
## See Also
|
|
272
|
-
|
|
273
|
-
- [History Overview](index.md)
|
|
274
|
-
- [Config](config.md)
|
|
275
|
-
- [ThreadManager](thread-manager.md)
|