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/history/index.md
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
# History
|
|
2
|
-
|
|
3
|
-
Conversation persistence and thread management.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
The history system enables persistent conversations by storing and retrieving conversation threads and results.
|
|
8
|
-
|
|
9
|
-
```ruby
|
|
10
|
-
network = RobotLab.create_network do
|
|
11
|
-
name "persistent_chat"
|
|
12
|
-
|
|
13
|
-
history History::Config.new(
|
|
14
|
-
create_thread: ->(state:, **) { Thread.create(id: SecureRandom.uuid) },
|
|
15
|
-
get: ->(thread_id:, **) { Thread.find(thread_id).results },
|
|
16
|
-
append_results: ->(thread_id:, new_results:, **) {
|
|
17
|
-
Thread.find(thread_id).results.concat(new_results)
|
|
18
|
-
}
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
add_robot assistant
|
|
22
|
-
end
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Components
|
|
26
|
-
|
|
27
|
-
| Component | Description |
|
|
28
|
-
|-----------|-------------|
|
|
29
|
-
| [Config](config.md) | History configuration |
|
|
30
|
-
| [ThreadManager](thread-manager.md) | Thread lifecycle management |
|
|
31
|
-
| [ActiveRecordAdapter](active-record-adapter.md) | Rails integration |
|
|
32
|
-
|
|
33
|
-
## Quick Start
|
|
34
|
-
|
|
35
|
-
### Basic Configuration
|
|
36
|
-
|
|
37
|
-
```ruby
|
|
38
|
-
history = History::Config.new(
|
|
39
|
-
create_thread: ->(state:, input:, **) {
|
|
40
|
-
{ id: SecureRandom.uuid }
|
|
41
|
-
},
|
|
42
|
-
get: ->(thread_id:, **) {
|
|
43
|
-
STORE[thread_id] || []
|
|
44
|
-
},
|
|
45
|
-
append_results: ->(thread_id:, new_results:, **) {
|
|
46
|
-
STORE[thread_id] ||= []
|
|
47
|
-
STORE[thread_id].concat(new_results)
|
|
48
|
-
}
|
|
49
|
-
)
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### With ActiveRecord
|
|
53
|
-
|
|
54
|
-
```ruby
|
|
55
|
-
history = History::ActiveRecordAdapter.new(
|
|
56
|
-
thread_model: ConversationThread,
|
|
57
|
-
result_model: ConversationResult
|
|
58
|
-
).to_config
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Callbacks
|
|
62
|
-
|
|
63
|
-
| Callback | Purpose |
|
|
64
|
-
|----------|---------|
|
|
65
|
-
| `create_thread` | Create new conversation thread |
|
|
66
|
-
| `get` | Retrieve existing thread history |
|
|
67
|
-
| `append_results` | Add results to thread |
|
|
68
|
-
|
|
69
|
-
## Thread Lifecycle
|
|
70
|
-
|
|
71
|
-
```mermaid
|
|
72
|
-
sequenceDiagram
|
|
73
|
-
participant U as User
|
|
74
|
-
participant N as Network
|
|
75
|
-
participant H as History
|
|
76
|
-
|
|
77
|
-
U->>N: Run with new message
|
|
78
|
-
N->>H: get(thread_id)
|
|
79
|
-
H-->>N: Previous results
|
|
80
|
-
N->>N: Execute robots
|
|
81
|
-
N->>H: append_results(new_results)
|
|
82
|
-
H-->>N: Saved
|
|
83
|
-
N-->>U: Result with thread_id
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## Examples
|
|
87
|
-
|
|
88
|
-
### In-Memory Store
|
|
89
|
-
|
|
90
|
-
```ruby
|
|
91
|
-
THREADS = {}
|
|
92
|
-
|
|
93
|
-
history = History::Config.new(
|
|
94
|
-
create_thread: ->(state:, **) {
|
|
95
|
-
id = SecureRandom.uuid
|
|
96
|
-
THREADS[id] = []
|
|
97
|
-
{ id: id }
|
|
98
|
-
},
|
|
99
|
-
get: ->(thread_id:, **) {
|
|
100
|
-
THREADS[thread_id] || []
|
|
101
|
-
},
|
|
102
|
-
append_results: ->(thread_id:, new_results:, **) {
|
|
103
|
-
THREADS[thread_id].concat(new_results)
|
|
104
|
-
}
|
|
105
|
-
)
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Redis Store
|
|
109
|
-
|
|
110
|
-
```ruby
|
|
111
|
-
history = History::Config.new(
|
|
112
|
-
create_thread: ->(state:, **) {
|
|
113
|
-
id = SecureRandom.uuid
|
|
114
|
-
Redis.current.set("thread:#{id}", [].to_json)
|
|
115
|
-
{ id: id }
|
|
116
|
-
},
|
|
117
|
-
get: ->(thread_id:, **) {
|
|
118
|
-
data = Redis.current.get("thread:#{thread_id}")
|
|
119
|
-
data ? JSON.parse(data) : []
|
|
120
|
-
},
|
|
121
|
-
append_results: ->(thread_id:, new_results:, **) {
|
|
122
|
-
existing = JSON.parse(Redis.current.get("thread:#{thread_id}") || "[]")
|
|
123
|
-
existing.concat(new_results.map(&:to_h))
|
|
124
|
-
Redis.current.set("thread:#{thread_id}", existing.to_json)
|
|
125
|
-
}
|
|
126
|
-
)
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
## See Also
|
|
130
|
-
|
|
131
|
-
- [History Guide](../../guides/history.md)
|
|
132
|
-
- [State](../core/state.md)
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
# ThreadManager
|
|
2
|
-
|
|
3
|
-
Manages conversation thread lifecycle.
|
|
4
|
-
|
|
5
|
-
## Class: `RobotLab::History::ThreadManager`
|
|
6
|
-
|
|
7
|
-
```ruby
|
|
8
|
-
manager = History::ThreadManager.new(config: history_config)
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Constructor
|
|
12
|
-
|
|
13
|
-
```ruby
|
|
14
|
-
ThreadManager.new(config:)
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
**Parameters:**
|
|
18
|
-
|
|
19
|
-
| Name | Type | Description |
|
|
20
|
-
|------|------|-------------|
|
|
21
|
-
| `config` | `Config` | History configuration |
|
|
22
|
-
|
|
23
|
-
## Methods
|
|
24
|
-
|
|
25
|
-
### create_thread
|
|
26
|
-
|
|
27
|
-
```ruby
|
|
28
|
-
thread_info = manager.create_thread(state: state, input: input, **context)
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
Create a new conversation thread.
|
|
32
|
-
|
|
33
|
-
**Returns:** Hash with `:id` and optional metadata.
|
|
34
|
-
|
|
35
|
-
### get_history
|
|
36
|
-
|
|
37
|
-
```ruby
|
|
38
|
-
results = manager.get_history(thread_id: id, **context)
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Retrieve conversation history.
|
|
42
|
-
|
|
43
|
-
**Returns:** Array of `RobotResult`.
|
|
44
|
-
|
|
45
|
-
### append_results
|
|
46
|
-
|
|
47
|
-
```ruby
|
|
48
|
-
manager.append_results(thread_id: id, new_results: results, **context)
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Add results to a thread.
|
|
52
|
-
|
|
53
|
-
### ensure_thread
|
|
54
|
-
|
|
55
|
-
```ruby
|
|
56
|
-
thread_id = manager.ensure_thread(state: state, input: input, **context)
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Create thread if state doesn't have one, or return existing.
|
|
60
|
-
|
|
61
|
-
### load_history
|
|
62
|
-
|
|
63
|
-
```ruby
|
|
64
|
-
state = manager.load_history(state: state, **context)
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
Load history into state if thread_id exists.
|
|
68
|
-
|
|
69
|
-
## Examples
|
|
70
|
-
|
|
71
|
-
### Basic Usage
|
|
72
|
-
|
|
73
|
-
```ruby
|
|
74
|
-
config = History::Config.new(...)
|
|
75
|
-
manager = History::ThreadManager.new(config: config)
|
|
76
|
-
|
|
77
|
-
# Start new conversation
|
|
78
|
-
state = RobotLab.create_state(message: "Hello")
|
|
79
|
-
thread_id = manager.ensure_thread(state: state, input: "Hello")
|
|
80
|
-
state.thread_id = thread_id
|
|
81
|
-
|
|
82
|
-
# Run and save
|
|
83
|
-
result = network.run(state: state)
|
|
84
|
-
manager.append_results(thread_id: thread_id, new_results: result.new_results)
|
|
85
|
-
|
|
86
|
-
# Continue conversation
|
|
87
|
-
state2 = RobotLab.create_state(message: "Follow up")
|
|
88
|
-
state2.thread_id = thread_id
|
|
89
|
-
state2 = manager.load_history(state: state2)
|
|
90
|
-
# state2 now has previous results
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
### In Network
|
|
94
|
-
|
|
95
|
-
```ruby
|
|
96
|
-
# ThreadManager is used internally by Network
|
|
97
|
-
network = RobotLab.create_network do
|
|
98
|
-
history config
|
|
99
|
-
add_robot assistant
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
# First message - thread created automatically
|
|
103
|
-
result1 = network.run(state: state1)
|
|
104
|
-
thread_id = result1.state.thread_id
|
|
105
|
-
|
|
106
|
-
# Continue - history loaded automatically
|
|
107
|
-
state2 = RobotLab.create_state(
|
|
108
|
-
message: UserMessage.new("Continue", thread_id: thread_id)
|
|
109
|
-
)
|
|
110
|
-
result2 = network.run(state: state2)
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Custom Thread Data
|
|
114
|
-
|
|
115
|
-
```ruby
|
|
116
|
-
manager = History::ThreadManager.new(
|
|
117
|
-
config: History::Config.new(
|
|
118
|
-
create_thread: ->(state:, input:, metadata:, **) {
|
|
119
|
-
Thread.create(
|
|
120
|
-
title: input.truncate(50),
|
|
121
|
-
metadata: metadata,
|
|
122
|
-
created_at: Time.current
|
|
123
|
-
)
|
|
124
|
-
},
|
|
125
|
-
get: ->(thread_id:, **) { Thread.find(thread_id).results },
|
|
126
|
-
append_results: ->(thread_id:, new_results:, **) {
|
|
127
|
-
Thread.find(thread_id).results.concat(new_results)
|
|
128
|
-
}
|
|
129
|
-
)
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
# Pass metadata when creating thread
|
|
133
|
-
thread = manager.create_thread(
|
|
134
|
-
state: state,
|
|
135
|
-
input: "Help with billing",
|
|
136
|
-
metadata: { source: "web", priority: "high" }
|
|
137
|
-
)
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
## See Also
|
|
141
|
-
|
|
142
|
-
- [History Overview](index.md)
|
|
143
|
-
- [Config](config.md)
|
|
144
|
-
- [ActiveRecordAdapter](active-record-adapter.md)
|
data/docs/guides/history.md
DELETED
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
# Conversation History
|
|
2
|
-
|
|
3
|
-
Persist and restore conversation threads across sessions.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
History allows you to:
|
|
8
|
-
|
|
9
|
-
- Save conversation results to a database
|
|
10
|
-
- Restore previous conversations
|
|
11
|
-
- Continue multi-turn interactions
|
|
12
|
-
- Maintain context across sessions
|
|
13
|
-
|
|
14
|
-
## Configuration
|
|
15
|
-
|
|
16
|
-
### History Config
|
|
17
|
-
|
|
18
|
-
Configure history with callbacks:
|
|
19
|
-
|
|
20
|
-
```ruby
|
|
21
|
-
history_config = RobotLab::History::Config.new(
|
|
22
|
-
create_thread: ->(state:, input:, **) {
|
|
23
|
-
# Create a new thread, return thread_id
|
|
24
|
-
{ thread_id: SecureRandom.uuid }
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
get: ->(thread_id:, **) {
|
|
28
|
-
# Retrieve history for thread
|
|
29
|
-
# Return Array<RobotResult>
|
|
30
|
-
[]
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
append_user_message: ->(thread_id:, message:, **) {
|
|
34
|
-
# Optional: Store user message
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
append_results: ->(thread_id:, new_results:, **) {
|
|
38
|
-
# Store new results
|
|
39
|
-
}
|
|
40
|
-
)
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
### Apply to Network
|
|
44
|
-
|
|
45
|
-
```ruby
|
|
46
|
-
network = RobotLab.create_network do
|
|
47
|
-
name "persistent_chat"
|
|
48
|
-
history history_config
|
|
49
|
-
end
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Callback Reference
|
|
53
|
-
|
|
54
|
-
### create_thread
|
|
55
|
-
|
|
56
|
-
Called when starting a new conversation:
|
|
57
|
-
|
|
58
|
-
```ruby
|
|
59
|
-
create_thread: ->(state:, input:, **kwargs) {
|
|
60
|
-
# state - Current State object
|
|
61
|
-
# input - UserMessage or string
|
|
62
|
-
# kwargs - Additional context
|
|
63
|
-
|
|
64
|
-
thread = Thread.create!(
|
|
65
|
-
initial_input: input.to_s,
|
|
66
|
-
user_id: state.data[:user_id]
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
{ thread_id: thread.id.to_s } # Must return hash with :thread_id
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### get
|
|
74
|
-
|
|
75
|
-
Called to retrieve existing history:
|
|
76
|
-
|
|
77
|
-
```ruby
|
|
78
|
-
get: ->(thread_id:, **kwargs) {
|
|
79
|
-
# thread_id - The thread identifier
|
|
80
|
-
# kwargs - Additional context
|
|
81
|
-
|
|
82
|
-
Result.where(thread_id: thread_id)
|
|
83
|
-
.order(:created_at)
|
|
84
|
-
.map { |r| deserialize_result(r) }
|
|
85
|
-
|
|
86
|
-
# Must return Array<RobotResult>
|
|
87
|
-
}
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### append_user_message (Optional)
|
|
91
|
-
|
|
92
|
-
Called when a user message is added:
|
|
93
|
-
|
|
94
|
-
```ruby
|
|
95
|
-
append_user_message: ->(thread_id:, message:, **kwargs) {
|
|
96
|
-
# thread_id - The thread identifier
|
|
97
|
-
# message - UserMessage object
|
|
98
|
-
|
|
99
|
-
Message.create!(
|
|
100
|
-
thread_id: thread_id,
|
|
101
|
-
content: message.content,
|
|
102
|
-
metadata: message.metadata
|
|
103
|
-
)
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### append_results
|
|
108
|
-
|
|
109
|
-
Called after robots finish:
|
|
110
|
-
|
|
111
|
-
```ruby
|
|
112
|
-
append_results: ->(thread_id:, new_results:, **kwargs) {
|
|
113
|
-
# thread_id - The thread identifier
|
|
114
|
-
# new_results - Array<RobotResult>
|
|
115
|
-
|
|
116
|
-
new_results.each do |result|
|
|
117
|
-
Result.create!(
|
|
118
|
-
thread_id: thread_id,
|
|
119
|
-
robot_name: result.robot_name,
|
|
120
|
-
output_data: serialize_output(result.output),
|
|
121
|
-
stop_reason: result.stop_reason
|
|
122
|
-
)
|
|
123
|
-
end
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## ActiveRecord Adapter
|
|
128
|
-
|
|
129
|
-
RobotLab includes a built-in ActiveRecord adapter:
|
|
130
|
-
|
|
131
|
-
```ruby
|
|
132
|
-
adapter = RobotLab::History::ActiveRecordAdapter.new(
|
|
133
|
-
thread_model: RobotLabThread,
|
|
134
|
-
result_model: RobotLabResult
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
network = RobotLab.create_network do
|
|
138
|
-
history adapter.to_config
|
|
139
|
-
end
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Required Models
|
|
143
|
-
|
|
144
|
-
```ruby title="app/models/robot_lab_thread.rb"
|
|
145
|
-
class RobotLabThread < ApplicationRecord
|
|
146
|
-
has_many :results, class_name: "RobotLabResult", foreign_key: :thread_id
|
|
147
|
-
|
|
148
|
-
# Required columns:
|
|
149
|
-
# - thread_id: string
|
|
150
|
-
# - initial_input: text
|
|
151
|
-
# - input_metadata: jsonb
|
|
152
|
-
# - state_data: jsonb
|
|
153
|
-
# - last_user_message: text
|
|
154
|
-
# - last_user_message_at: datetime
|
|
155
|
-
end
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
```ruby title="app/models/robot_lab_result.rb"
|
|
159
|
-
class RobotLabResult < ApplicationRecord
|
|
160
|
-
belongs_to :thread, class_name: "RobotLabThread", foreign_key: :thread_id
|
|
161
|
-
|
|
162
|
-
# Required columns:
|
|
163
|
-
# - thread_id: string
|
|
164
|
-
# - robot_name: string
|
|
165
|
-
# - sequence_number: integer
|
|
166
|
-
# - output_data: jsonb
|
|
167
|
-
# - tool_calls_data: jsonb
|
|
168
|
-
# - stop_reason: string
|
|
169
|
-
# - checksum: string
|
|
170
|
-
end
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
## Using Thread IDs
|
|
174
|
-
|
|
175
|
-
### Start New Thread
|
|
176
|
-
|
|
177
|
-
```ruby
|
|
178
|
-
state = RobotLab.create_state(message: "Hello!")
|
|
179
|
-
result = network.run(state: state)
|
|
180
|
-
|
|
181
|
-
# Thread ID is assigned automatically
|
|
182
|
-
thread_id = state.thread_id
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
### Continue Existing Thread
|
|
186
|
-
|
|
187
|
-
```ruby
|
|
188
|
-
# Option 1: Via UserMessage
|
|
189
|
-
message = RobotLab::UserMessage.new(
|
|
190
|
-
"Continue our conversation",
|
|
191
|
-
thread_id: existing_thread_id
|
|
192
|
-
)
|
|
193
|
-
state = RobotLab.create_state(message: message)
|
|
194
|
-
|
|
195
|
-
# Option 2: Direct assignment
|
|
196
|
-
state = RobotLab.create_state(message: "Continue")
|
|
197
|
-
state.thread_id = existing_thread_id
|
|
198
|
-
|
|
199
|
-
# History is automatically loaded
|
|
200
|
-
result = network.run(state: state)
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
## ThreadManager
|
|
204
|
-
|
|
205
|
-
For programmatic control:
|
|
206
|
-
|
|
207
|
-
```ruby
|
|
208
|
-
manager = RobotLab::History::ThreadManager.new(history_config)
|
|
209
|
-
|
|
210
|
-
# Create thread
|
|
211
|
-
thread_id = manager.create_thread(state: state, input: message)
|
|
212
|
-
|
|
213
|
-
# Load history
|
|
214
|
-
results = manager.get_history(thread_id)
|
|
215
|
-
|
|
216
|
-
# Save state
|
|
217
|
-
manager.save_state(thread_id: thread_id, state: state, since_index: 5)
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
## Serialization
|
|
221
|
-
|
|
222
|
-
### RobotResult
|
|
223
|
-
|
|
224
|
-
Results are serialized via `export`:
|
|
225
|
-
|
|
226
|
-
```ruby
|
|
227
|
-
result.export
|
|
228
|
-
# => {
|
|
229
|
-
# robot_name: "assistant",
|
|
230
|
-
# output: [...],
|
|
231
|
-
# tool_calls: [...],
|
|
232
|
-
# stop_reason: "stop",
|
|
233
|
-
# id: "...",
|
|
234
|
-
# created_at: "..."
|
|
235
|
-
# }
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### Messages
|
|
239
|
-
|
|
240
|
-
Messages serialize to hashes:
|
|
241
|
-
|
|
242
|
-
```ruby
|
|
243
|
-
message.to_h
|
|
244
|
-
# => {
|
|
245
|
-
# type: "text",
|
|
246
|
-
# role: "assistant",
|
|
247
|
-
# content: "Hello!",
|
|
248
|
-
# stop_reason: "stop"
|
|
249
|
-
# }
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
### Restore from hash
|
|
253
|
-
|
|
254
|
-
```ruby
|
|
255
|
-
RobotLab::Message.from_hash(hash)
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
## Patterns
|
|
259
|
-
|
|
260
|
-
### Redis-Based History
|
|
261
|
-
|
|
262
|
-
```ruby
|
|
263
|
-
history_config = History::Config.new(
|
|
264
|
-
create_thread: ->(state:, input:, **) {
|
|
265
|
-
thread_id = SecureRandom.uuid
|
|
266
|
-
Redis.current.hset("threads", thread_id, input.to_s)
|
|
267
|
-
{ thread_id: thread_id }
|
|
268
|
-
},
|
|
269
|
-
|
|
270
|
-
get: ->(thread_id:, **) {
|
|
271
|
-
data = Redis.current.lrange("results:#{thread_id}", 0, -1)
|
|
272
|
-
data.map { |json| deserialize_result(JSON.parse(json)) }
|
|
273
|
-
},
|
|
274
|
-
|
|
275
|
-
append_results: ->(thread_id:, new_results:, **) {
|
|
276
|
-
new_results.each do |result|
|
|
277
|
-
Redis.current.rpush("results:#{thread_id}", result.export.to_json)
|
|
278
|
-
end
|
|
279
|
-
}
|
|
280
|
-
)
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
### Custom Storage
|
|
284
|
-
|
|
285
|
-
```ruby
|
|
286
|
-
class CustomHistoryAdapter
|
|
287
|
-
def initialize(storage)
|
|
288
|
-
@storage = storage
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
def to_config
|
|
292
|
-
History::Config.new(
|
|
293
|
-
create_thread: method(:create_thread),
|
|
294
|
-
get: method(:get),
|
|
295
|
-
append_results: method(:append_results)
|
|
296
|
-
)
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
private
|
|
300
|
-
|
|
301
|
-
def create_thread(state:, input:, **)
|
|
302
|
-
id = @storage.create_conversation(input: input.to_s)
|
|
303
|
-
{ thread_id: id }
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
def get(thread_id:, **)
|
|
307
|
-
@storage.fetch_results(thread_id)
|
|
308
|
-
end
|
|
309
|
-
|
|
310
|
-
def append_results(thread_id:, new_results:, **)
|
|
311
|
-
@storage.store_results(thread_id, new_results)
|
|
312
|
-
end
|
|
313
|
-
end
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
## Best Practices
|
|
317
|
-
|
|
318
|
-
### 1. Handle Missing Threads
|
|
319
|
-
|
|
320
|
-
```ruby
|
|
321
|
-
get: ->(thread_id:, **) {
|
|
322
|
-
thread = Thread.find_by(thread_id: thread_id)
|
|
323
|
-
return [] unless thread
|
|
324
|
-
|
|
325
|
-
thread.results.order(:created_at).map(&:to_robot_result)
|
|
326
|
-
}
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
### 2. Index for Performance
|
|
330
|
-
|
|
331
|
-
```sql
|
|
332
|
-
CREATE INDEX idx_results_thread_id ON robot_lab_results(thread_id);
|
|
333
|
-
CREATE INDEX idx_results_created_at ON robot_lab_results(created_at);
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
### 3. Clean Up Old Threads
|
|
337
|
-
|
|
338
|
-
```ruby
|
|
339
|
-
# Periodic cleanup job
|
|
340
|
-
Thread.where("updated_at < ?", 30.days.ago).destroy_all
|
|
341
|
-
```
|
|
342
|
-
|
|
343
|
-
### 4. Limit History Size
|
|
344
|
-
|
|
345
|
-
```ruby
|
|
346
|
-
get: ->(thread_id:, **) {
|
|
347
|
-
Result.where(thread_id: thread_id)
|
|
348
|
-
.order(created_at: :desc)
|
|
349
|
-
.limit(50) # Last 50 exchanges
|
|
350
|
-
.reverse
|
|
351
|
-
.map(&:to_robot_result)
|
|
352
|
-
}
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
## Next Steps
|
|
356
|
-
|
|
357
|
-
- [Memory System](memory.md) - In-memory data sharing
|
|
358
|
-
- [State Management](../architecture/state-management.md) - State details
|
|
359
|
-
- [API Reference: History](../api/history/index.md) - Complete API
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<%= message %>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<%= message %>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<%= message %>
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
Customer <%= customer[:name] %> (escalated case): <%= message %>
|
|
2
|
-
|
|
3
|
-
## Case Context
|
|
4
|
-
<% if defined?(escalation_context) && escalation_context %>
|
|
5
|
-
<% if escalation_context[:previous_interactions]&.any? %>
|
|
6
|
-
### Previous Interactions
|
|
7
|
-
<% escalation_context[:previous_interactions].each do |interaction| %>
|
|
8
|
-
- <%= interaction[:date] %> (<%= interaction[:channel] %>): <%= interaction[:summary] %>
|
|
9
|
-
<% end %>
|
|
10
|
-
<% end %>
|
|
11
|
-
|
|
12
|
-
<% if escalation_context[:related_orders]&.any? %>
|
|
13
|
-
### Related Orders
|
|
14
|
-
<% escalation_context[:related_orders].each do |order| %>
|
|
15
|
-
- Order #<%= order[:id] %>: $<%= order[:total] %> - <%= order[:status] %>
|
|
16
|
-
<% end %>
|
|
17
|
-
<% end %>
|
|
18
|
-
|
|
19
|
-
<% if escalation_context[:compensation_history]&.any? %>
|
|
20
|
-
### Previous Compensation
|
|
21
|
-
<% escalation_context[:compensation_history].each do |comp| %>
|
|
22
|
-
- <%= comp[:date] %>: <%= comp[:type] %> - $<%= comp[:amount] %> (<%= comp[:reason] %>)
|
|
23
|
-
<% end %>
|
|
24
|
-
<% end %>
|
|
25
|
-
|
|
26
|
-
### Escalation Reason
|
|
27
|
-
<%= escalation_context[:escalation_reason] || "Not specified" %>
|
|
28
|
-
|
|
29
|
-
### Sentiment Analysis
|
|
30
|
-
- Detected Sentiment: <%= escalation_context[:sentiment] || "Unknown" %>
|
|
31
|
-
- Urgency Level: <%= escalation_context[:urgency] || "Normal" %>
|
|
32
|
-
<% end %>
|
|
33
|
-
|
|
34
|
-
Please resolve this escalated case with empathy and authority.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<%= message %>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<%= message %>
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<%= message %>
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
Customer <%= customer[:name] %> asks: <%= message %>
|
|
2
|
-
|
|
3
|
-
## Order History
|
|
4
|
-
<% if orders&.any? %>
|
|
5
|
-
<% orders.each do |order| %>
|
|
6
|
-
### Order #<%= order[:id] %>
|
|
7
|
-
- Date: <%= order[:date] %>
|
|
8
|
-
- Status: <%= order[:status] %>
|
|
9
|
-
- Total: $<%= order[:total] %>
|
|
10
|
-
- Items:
|
|
11
|
-
<% order[:items].each do |item| %>
|
|
12
|
-
- <%= item[:name] %> (x<%= item[:quantity] %>) - $<%= item[:price] %>
|
|
13
|
-
<% end %>
|
|
14
|
-
<% if order[:tracking] %>
|
|
15
|
-
- Tracking: <%= order[:tracking] %>
|
|
16
|
-
<% end %>
|
|
17
|
-
<% end %>
|
|
18
|
-
<% else %>
|
|
19
|
-
No orders found for this customer.
|
|
20
|
-
<% end %>
|
|
21
|
-
|
|
22
|
-
Please help the customer with their order inquiry.
|