robot_lab 0.0.1 → 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/.github/workflows/deploy-github-pages.yml +9 -9
- data/.irbrc +6 -0
- data/CHANGELOG.md +140 -0
- data/README.md +263 -48
- data/Rakefile +71 -1
- data/docs/api/core/index.md +53 -46
- data/docs/api/core/memory.md +200 -154
- data/docs/api/core/network.md +13 -3
- data/docs/api/core/robot.md +490 -130
- data/docs/api/core/state.md +55 -73
- data/docs/api/core/tool.md +205 -209
- data/docs/api/index.md +7 -28
- data/docs/api/mcp/client.md +119 -48
- data/docs/api/mcp/index.md +75 -60
- data/docs/api/mcp/server.md +120 -136
- data/docs/api/mcp/transports.md +172 -184
- data/docs/api/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/api/streaming/context.md +157 -74
- data/docs/api/streaming/events.md +114 -166
- data/docs/api/streaming/index.md +74 -72
- data/docs/architecture/core-concepts.md +360 -116
- data/docs/architecture/index.md +97 -59
- data/docs/architecture/message-flow.md +138 -129
- data/docs/architecture/network-orchestration.md +197 -50
- data/docs/architecture/robot-execution.md +199 -146
- data/docs/architecture/state-management.md +255 -187
- data/docs/concepts.md +311 -49
- data/docs/examples/basic-chat.md +89 -77
- data/docs/examples/index.md +222 -47
- data/docs/examples/mcp-server.md +207 -203
- data/docs/examples/multi-robot-network.md +129 -35
- data/docs/examples/rails-application.md +159 -160
- data/docs/examples/tool-usage.md +295 -204
- data/docs/getting-started/configuration.md +347 -154
- data/docs/getting-started/index.md +1 -1
- data/docs/getting-started/installation.md +22 -13
- data/docs/getting-started/quick-start.md +166 -121
- data/docs/guides/building-robots.md +418 -212
- data/docs/guides/creating-networks.md +143 -24
- data/docs/guides/index.md +0 -5
- data/docs/guides/mcp-integration.md +152 -113
- data/docs/guides/memory.md +220 -164
- data/docs/guides/rails-integration.md +244 -162
- data/docs/guides/streaming.md +137 -187
- data/docs/guides/using-tools.md +259 -212
- data/docs/index.md +46 -41
- data/examples/01_simple_robot.rb +6 -9
- data/examples/02_tools.rb +6 -9
- data/examples/03_network.rb +19 -17
- data/examples/04_mcp.rb +5 -8
- data/examples/05_streaming.rb +5 -8
- data/examples/06_prompt_templates.rb +42 -37
- data/examples/07_network_memory.rb +13 -14
- data/examples/08_llm_config.rb +169 -0
- data/examples/09_chaining.rb +262 -0
- data/examples/10_memory.rb +331 -0
- data/examples/11_network_introspection.rb +253 -0
- data/examples/12_message_bus.rb +74 -0
- data/examples/13_spawn.rb +90 -0
- data/examples/14_rusty_circuit/comic.rb +143 -0
- data/examples/14_rusty_circuit/display.rb +203 -0
- data/examples/14_rusty_circuit/heckler.rb +63 -0
- data/examples/14_rusty_circuit/open_mic.rb +123 -0
- data/examples/14_rusty_circuit/prompts/open_mic_comic.md +20 -0
- data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +23 -0
- data/examples/14_rusty_circuit/prompts/open_mic_scout.md +20 -0
- data/examples/14_rusty_circuit/scout.rb +156 -0
- data/examples/14_rusty_circuit/scout_notes.md +89 -0
- data/examples/14_rusty_circuit/show.log +234 -0
- data/examples/15_memory_network_and_bus/editor_in_chief.rb +24 -0
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +206 -0
- data/examples/15_memory_network_and_bus/linux_writer.rb +80 -0
- data/examples/15_memory_network_and_bus/os_editor.rb +46 -0
- data/examples/15_memory_network_and_bus/os_writer.rb +46 -0
- data/examples/15_memory_network_and_bus/output/combined_article.md +13 -0
- data/examples/15_memory_network_and_bus/output/final_article.md +15 -0
- data/examples/15_memory_network_and_bus/output/linux_draft.md +5 -0
- data/examples/15_memory_network_and_bus/output/mac_draft.md +7 -0
- data/examples/15_memory_network_and_bus/output/memory.json +13 -0
- data/examples/15_memory_network_and_bus/output/revision_1.md +19 -0
- data/examples/15_memory_network_and_bus/output/revision_2.md +15 -0
- data/examples/15_memory_network_and_bus/output/windows_draft.md +7 -0
- data/examples/15_memory_network_and_bus/prompts/os_advocate.md +13 -0
- data/examples/15_memory_network_and_bus/prompts/os_chief.md +13 -0
- data/examples/15_memory_network_and_bus/prompts/os_editor.md +13 -0
- data/examples/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/examples/README.md +197 -0
- data/examples/prompts/{assistant/system.txt.erb → assistant.md} +3 -0
- data/examples/prompts/{billing/system.txt.erb → billing.md} +3 -0
- data/examples/prompts/{classifier/system.txt.erb → classifier.md} +3 -0
- data/examples/prompts/comedian.md +6 -0
- data/examples/prompts/comedy_critic.md +10 -0
- data/examples/prompts/configurable.md +9 -0
- data/examples/prompts/dispatcher.md +12 -0
- data/examples/prompts/{entity_extractor/system.txt.erb → entity_extractor.md} +3 -0
- data/examples/prompts/{escalation/system.txt.erb → escalation.md} +7 -0
- data/examples/prompts/frontmatter_mcp_test.md +9 -0
- data/examples/prompts/frontmatter_named_test.md +5 -0
- data/examples/prompts/frontmatter_tools_test.md +6 -0
- data/examples/prompts/{general/system.txt.erb → general.md} +3 -0
- data/examples/prompts/{github_assistant/system.txt.erb → github_assistant.md} +8 -0
- data/examples/prompts/{helper/system.txt.erb → helper.md} +3 -0
- data/examples/prompts/{keyword_extractor/system.txt.erb → keyword_extractor.md} +3 -0
- data/examples/prompts/llm_config_demo.md +20 -0
- data/examples/prompts/{order_support/system.txt.erb → order_support.md} +8 -0
- data/examples/prompts/os_advocate.md +13 -0
- data/examples/prompts/os_chief.md +13 -0
- data/examples/prompts/os_editor.md +13 -0
- data/examples/prompts/{product_support/system.txt.erb → product_support.md} +7 -0
- data/examples/prompts/{sentiment_analyzer/system.txt.erb → sentiment_analyzer.md} +3 -0
- data/examples/prompts/{synthesizer/system.txt.erb → synthesizer.md} +3 -0
- data/examples/prompts/{technical/system.txt.erb → technical.md} +3 -0
- data/examples/prompts/{triage/system.txt.erb → triage.md} +6 -0
- data/lib/generators/robot_lab/templates/initializer.rb.tt +0 -13
- data/lib/robot_lab/ask_user.rb +75 -0
- data/lib/robot_lab/config/defaults.yml +121 -0
- data/lib/robot_lab/config.rb +183 -0
- data/lib/robot_lab/error.rb +6 -0
- data/lib/robot_lab/mcp/client.rb +1 -1
- data/lib/robot_lab/memory.rb +10 -34
- 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 +240 -330
- data/lib/robot_lab/robot_message.rb +44 -0
- data/lib/robot_lab/robot_result.rb +1 -0
- data/lib/robot_lab/run_config.rb +184 -0
- data/lib/robot_lab/state_proxy.rb +2 -12
- data/lib/robot_lab/streaming/context.rb +1 -1
- data/lib/robot_lab/task.rb +8 -1
- data/lib/robot_lab/tool.rb +108 -172
- data/lib/robot_lab/tool_config.rb +1 -1
- data/lib/robot_lab/tool_manifest.rb +2 -18
- data/lib/robot_lab/utils.rb +39 -0
- data/lib/robot_lab/version.rb +1 -1
- data/lib/robot_lab.rb +89 -57
- data/mkdocs.yml +0 -11
- metadata +121 -135
- 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 -195
- data/docs/api/history/config.md +0 -191
- data/docs/api/history/index.md +0 -132
- data/docs/api/history/thread-manager.md +0 -144
- data/docs/guides/history.md +0 -359
- data/examples/prompts/assistant/user.txt.erb +0 -1
- data/examples/prompts/billing/user.txt.erb +0 -1
- data/examples/prompts/classifier/user.txt.erb +0 -1
- data/examples/prompts/entity_extractor/user.txt.erb +0 -3
- data/examples/prompts/escalation/user.txt.erb +0 -34
- data/examples/prompts/general/user.txt.erb +0 -1
- data/examples/prompts/github_assistant/user.txt.erb +0 -1
- data/examples/prompts/helper/user.txt.erb +0 -1
- data/examples/prompts/keyword_extractor/user.txt.erb +0 -3
- data/examples/prompts/order_support/user.txt.erb +0 -22
- data/examples/prompts/product_support/user.txt.erb +0 -32
- data/examples/prompts/sentiment_analyzer/user.txt.erb +0 -3
- data/examples/prompts/synthesizer/user.txt.erb +0 -15
- data/examples/prompts/technical/user.txt.erb +0 -1
- data/examples/prompts/triage/user.txt.erb +0 -17
- data/lib/robot_lab/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 -159
- data/lib/robot_lab/adapters/registry.rb +0 -81
- data/lib/robot_lab/configuration.rb +0 -143
- 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/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,195 +0,0 @@
|
|
|
1
|
-
# ActiveRecordAdapter
|
|
2
|
-
|
|
3
|
-
Rails ActiveRecord integration for conversation persistence.
|
|
4
|
-
|
|
5
|
-
## Class: `RobotLab::History::ActiveRecordAdapter`
|
|
6
|
-
|
|
7
|
-
```ruby
|
|
8
|
-
adapter = History::ActiveRecordAdapter.new(
|
|
9
|
-
thread_model: ConversationThread,
|
|
10
|
-
result_model: ConversationResult
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
config = adapter.to_config
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Constructor
|
|
17
|
-
|
|
18
|
-
```ruby
|
|
19
|
-
ActiveRecordAdapter.new(
|
|
20
|
-
thread_model:,
|
|
21
|
-
result_model:,
|
|
22
|
-
thread_factory: nil,
|
|
23
|
-
result_factory: nil
|
|
24
|
-
)
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
**Parameters:**
|
|
28
|
-
|
|
29
|
-
| Name | Type | Description |
|
|
30
|
-
|------|------|-------------|
|
|
31
|
-
| `thread_model` | `Class` | ActiveRecord model for threads |
|
|
32
|
-
| `result_model` | `Class` | ActiveRecord model for results |
|
|
33
|
-
| `thread_factory` | `Proc`, `nil` | Custom thread creation |
|
|
34
|
-
| `result_factory` | `Proc`, `nil` | Custom result creation |
|
|
35
|
-
|
|
36
|
-
## Methods
|
|
37
|
-
|
|
38
|
-
### to_config
|
|
39
|
-
|
|
40
|
-
```ruby
|
|
41
|
-
config = adapter.to_config
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Convert to `History::Config` for use with networks.
|
|
45
|
-
|
|
46
|
-
## Model Requirements
|
|
47
|
-
|
|
48
|
-
### Thread Model
|
|
49
|
-
|
|
50
|
-
```ruby
|
|
51
|
-
# db/migrate/xxx_create_conversation_threads.rb
|
|
52
|
-
create_table :conversation_threads do |t|
|
|
53
|
-
t.string :external_id, null: false, index: { unique: true }
|
|
54
|
-
t.jsonb :metadata, default: {}
|
|
55
|
-
t.timestamps
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# app/models/conversation_thread.rb
|
|
59
|
-
class ConversationThread < ApplicationRecord
|
|
60
|
-
has_many :results, class_name: "ConversationResult",
|
|
61
|
-
foreign_key: :thread_id, dependent: :destroy
|
|
62
|
-
end
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Result Model
|
|
66
|
-
|
|
67
|
-
```ruby
|
|
68
|
-
# db/migrate/xxx_create_conversation_results.rb
|
|
69
|
-
create_table :conversation_results do |t|
|
|
70
|
-
t.references :thread, foreign_key: { to_table: :conversation_threads }
|
|
71
|
-
t.string :robot_name
|
|
72
|
-
t.jsonb :input, default: {}
|
|
73
|
-
t.jsonb :output, default: []
|
|
74
|
-
t.jsonb :tool_calls, default: []
|
|
75
|
-
t.jsonb :metadata, default: {}
|
|
76
|
-
t.integer :position
|
|
77
|
-
t.timestamps
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# app/models/conversation_result.rb
|
|
81
|
-
class ConversationResult < ApplicationRecord
|
|
82
|
-
belongs_to :thread, class_name: "ConversationThread"
|
|
83
|
-
|
|
84
|
-
def to_robot_result
|
|
85
|
-
RobotLab::RobotResult.from_hash(attributes)
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## Examples
|
|
91
|
-
|
|
92
|
-
### Basic Setup
|
|
93
|
-
|
|
94
|
-
```ruby
|
|
95
|
-
adapter = History::ActiveRecordAdapter.new(
|
|
96
|
-
thread_model: ConversationThread,
|
|
97
|
-
result_model: ConversationResult
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
network = RobotLab.create_network do
|
|
101
|
-
name "chat"
|
|
102
|
-
history adapter.to_config
|
|
103
|
-
add_robot assistant
|
|
104
|
-
end
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### With Custom Factory
|
|
108
|
-
|
|
109
|
-
```ruby
|
|
110
|
-
adapter = History::ActiveRecordAdapter.new(
|
|
111
|
-
thread_model: ConversationThread,
|
|
112
|
-
result_model: ConversationResult,
|
|
113
|
-
thread_factory: ->(state:, input:, **context) {
|
|
114
|
-
ConversationThread.create!(
|
|
115
|
-
external_id: SecureRandom.uuid,
|
|
116
|
-
user_id: context[:user_id],
|
|
117
|
-
title: input.truncate(100),
|
|
118
|
-
metadata: { source: context[:source] }
|
|
119
|
-
)
|
|
120
|
-
}
|
|
121
|
-
)
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### With User Scoping
|
|
125
|
-
|
|
126
|
-
```ruby
|
|
127
|
-
class ScopedAdapter
|
|
128
|
-
def initialize(thread_model:, result_model:)
|
|
129
|
-
@thread_model = thread_model
|
|
130
|
-
@result_model = result_model
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def to_config
|
|
134
|
-
History::Config.new(
|
|
135
|
-
create_thread: method(:create_thread),
|
|
136
|
-
get: method(:get),
|
|
137
|
-
append_results: method(:append_results)
|
|
138
|
-
)
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
private
|
|
142
|
-
|
|
143
|
-
def create_thread(state:, input:, user_id:, **)
|
|
144
|
-
@thread_model.create!(
|
|
145
|
-
external_id: SecureRandom.uuid,
|
|
146
|
-
user_id: user_id,
|
|
147
|
-
title: input.truncate(100)
|
|
148
|
-
)
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def get(thread_id:, user_id:, **)
|
|
152
|
-
thread = @thread_model.find_by(external_id: thread_id, user_id: user_id)
|
|
153
|
-
return [] unless thread
|
|
154
|
-
thread.results.order(:position).map(&:to_robot_result)
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def append_results(thread_id:, new_results:, user_id:, **)
|
|
158
|
-
thread = @thread_model.find_by!(external_id: thread_id, user_id: user_id)
|
|
159
|
-
position = thread.results.maximum(:position) || 0
|
|
160
|
-
|
|
161
|
-
@result_model.transaction do
|
|
162
|
-
new_results.each_with_index do |result, i|
|
|
163
|
-
thread.results.create!(
|
|
164
|
-
robot_name: result.robot_name,
|
|
165
|
-
input: result.input.to_h,
|
|
166
|
-
output: result.output.map(&:to_h),
|
|
167
|
-
tool_calls: result.tool_calls.map(&:to_h),
|
|
168
|
-
position: position + i + 1
|
|
169
|
-
)
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### Rails Generator
|
|
177
|
-
|
|
178
|
-
Use the Rails generator to create models:
|
|
179
|
-
|
|
180
|
-
```bash
|
|
181
|
-
rails generate robot_lab:history
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
This creates:
|
|
185
|
-
|
|
186
|
-
- `ConversationThread` model
|
|
187
|
-
- `ConversationResult` model
|
|
188
|
-
- Database migrations
|
|
189
|
-
- Initializer configuration
|
|
190
|
-
|
|
191
|
-
## See Also
|
|
192
|
-
|
|
193
|
-
- [History Overview](index.md)
|
|
194
|
-
- [Config](config.md)
|
|
195
|
-
- [Rails Integration Guide](../../guides/rails-integration.md)
|
data/docs/api/history/config.md
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
# History::Config
|
|
2
|
-
|
|
3
|
-
Configuration for conversation persistence.
|
|
4
|
-
|
|
5
|
-
## Class: `RobotLab::History::Config`
|
|
6
|
-
|
|
7
|
-
```ruby
|
|
8
|
-
config = History::Config.new(
|
|
9
|
-
create_thread: create_proc,
|
|
10
|
-
get: get_proc,
|
|
11
|
-
append_results: append_proc
|
|
12
|
-
)
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Constructor
|
|
16
|
-
|
|
17
|
-
```ruby
|
|
18
|
-
Config.new(
|
|
19
|
-
create_thread:,
|
|
20
|
-
get:,
|
|
21
|
-
append_results:
|
|
22
|
-
)
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
**Parameters:**
|
|
26
|
-
|
|
27
|
-
| Name | Type | Description |
|
|
28
|
-
|------|------|-------------|
|
|
29
|
-
| `create_thread` | `Proc` | Creates a new thread |
|
|
30
|
-
| `get` | `Proc` | Retrieves thread history |
|
|
31
|
-
| `append_results` | `Proc` | Appends results to thread |
|
|
32
|
-
|
|
33
|
-
## Callbacks
|
|
34
|
-
|
|
35
|
-
### create_thread
|
|
36
|
-
|
|
37
|
-
Called when a new conversation starts without a thread_id.
|
|
38
|
-
|
|
39
|
-
```ruby
|
|
40
|
-
create_thread: ->(state:, input:, **context) {
|
|
41
|
-
# Create and return thread info
|
|
42
|
-
{ id: SecureRandom.uuid }
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
**Arguments:**
|
|
47
|
-
|
|
48
|
-
| Name | Type | Description |
|
|
49
|
-
|------|------|-------------|
|
|
50
|
-
| `state` | `State` | Current state |
|
|
51
|
-
| `input` | `String` | User input |
|
|
52
|
-
| `**context` | `Hash` | Additional context |
|
|
53
|
-
|
|
54
|
-
**Returns:** Hash with `:id` key.
|
|
55
|
-
|
|
56
|
-
### get
|
|
57
|
-
|
|
58
|
-
Called to retrieve existing conversation history.
|
|
59
|
-
|
|
60
|
-
```ruby
|
|
61
|
-
get: ->(thread_id:, **context) {
|
|
62
|
-
# Return array of previous results
|
|
63
|
-
Thread.find(thread_id).results
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
**Arguments:**
|
|
68
|
-
|
|
69
|
-
| Name | Type | Description |
|
|
70
|
-
|------|------|-------------|
|
|
71
|
-
| `thread_id` | `String` | Thread identifier |
|
|
72
|
-
| `**context` | `Hash` | Additional context |
|
|
73
|
-
|
|
74
|
-
**Returns:** Array of `RobotResult` or hashes.
|
|
75
|
-
|
|
76
|
-
### append_results
|
|
77
|
-
|
|
78
|
-
Called after each network run to persist new results.
|
|
79
|
-
|
|
80
|
-
```ruby
|
|
81
|
-
append_results: ->(thread_id:, new_results:, **context) {
|
|
82
|
-
# Persist the new results
|
|
83
|
-
thread = Thread.find(thread_id)
|
|
84
|
-
new_results.each { |r| thread.results.create(r.to_h) }
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
**Arguments:**
|
|
89
|
-
|
|
90
|
-
| Name | Type | Description |
|
|
91
|
-
|------|------|-------------|
|
|
92
|
-
| `thread_id` | `String` | Thread identifier |
|
|
93
|
-
| `new_results` | `Array<RobotResult>` | Results to append |
|
|
94
|
-
| `**context` | `Hash` | Additional context |
|
|
95
|
-
|
|
96
|
-
## Attributes
|
|
97
|
-
|
|
98
|
-
### create_thread
|
|
99
|
-
|
|
100
|
-
```ruby
|
|
101
|
-
config.create_thread # => Proc
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### get
|
|
105
|
-
|
|
106
|
-
```ruby
|
|
107
|
-
config.get # => Proc
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### append_results
|
|
111
|
-
|
|
112
|
-
```ruby
|
|
113
|
-
config.append_results # => Proc
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
## Examples
|
|
117
|
-
|
|
118
|
-
### Basic Config
|
|
119
|
-
|
|
120
|
-
```ruby
|
|
121
|
-
STORE = {}
|
|
122
|
-
|
|
123
|
-
config = History::Config.new(
|
|
124
|
-
create_thread: ->(state:, **) {
|
|
125
|
-
id = SecureRandom.uuid
|
|
126
|
-
STORE[id] = { results: [] }
|
|
127
|
-
{ id: id }
|
|
128
|
-
},
|
|
129
|
-
|
|
130
|
-
get: ->(thread_id:, **) {
|
|
131
|
-
STORE.dig(thread_id, :results) || []
|
|
132
|
-
},
|
|
133
|
-
|
|
134
|
-
append_results: ->(thread_id:, new_results:, **) {
|
|
135
|
-
STORE[thread_id][:results].concat(new_results.map(&:to_h))
|
|
136
|
-
}
|
|
137
|
-
)
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### With Context
|
|
141
|
-
|
|
142
|
-
```ruby
|
|
143
|
-
config = History::Config.new(
|
|
144
|
-
create_thread: ->(state:, user_id:, **) {
|
|
145
|
-
Thread.create(user_id: user_id, started_at: Time.current)
|
|
146
|
-
},
|
|
147
|
-
|
|
148
|
-
get: ->(thread_id:, user_id:, **) {
|
|
149
|
-
Thread.where(id: thread_id, user_id: user_id).first&.results || []
|
|
150
|
-
},
|
|
151
|
-
|
|
152
|
-
append_results: ->(thread_id:, new_results:, user_id:, **) {
|
|
153
|
-
thread = Thread.find_by(id: thread_id, user_id: user_id)
|
|
154
|
-
return unless thread
|
|
155
|
-
new_results.each { |r| thread.results.create(r.to_h) }
|
|
156
|
-
}
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
# Pass context when running
|
|
160
|
-
network.run(state: state, user_id: current_user.id)
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### With Validation
|
|
164
|
-
|
|
165
|
-
```ruby
|
|
166
|
-
config = History::Config.new(
|
|
167
|
-
create_thread: ->(state:, **) {
|
|
168
|
-
raise "Invalid state" unless state.data[:user_id]
|
|
169
|
-
Thread.create(user_id: state.data[:user_id])
|
|
170
|
-
},
|
|
171
|
-
|
|
172
|
-
get: ->(thread_id:, **) {
|
|
173
|
-
thread = Thread.find_by(id: thread_id)
|
|
174
|
-
raise "Thread not found" unless thread
|
|
175
|
-
thread.results
|
|
176
|
-
},
|
|
177
|
-
|
|
178
|
-
append_results: ->(thread_id:, new_results:, **) {
|
|
179
|
-
thread = Thread.find(thread_id)
|
|
180
|
-
Thread.transaction do
|
|
181
|
-
new_results.each { |r| thread.results.create!(r.to_h) }
|
|
182
|
-
end
|
|
183
|
-
}
|
|
184
|
-
)
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## See Also
|
|
188
|
-
|
|
189
|
-
- [History Overview](index.md)
|
|
190
|
-
- [ThreadManager](thread-manager.md)
|
|
191
|
-
- [ActiveRecordAdapter](active-record-adapter.md)
|