robot_lab 0.3.0 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +1 -1
- data/lib/robot_lab/mcp/transports/streamable_http.rb +1 -1
- data/lib/robot_lab/version.rb +1 -1
- metadata +3 -253
- data/.architecture/AGENTS.md +0 -32
- data/.architecture/config.yml +0 -8
- data/.architecture/members.yml +0 -60
- data/.architecture/reviews/feature-free-will.md +0 -490
- data/.architecture/reviews/overall-codebase.md +0 -427
- data/.claude/settings.local.json +0 -57
- data/.codex/config.toml +0 -2
- data/.envrc +0 -6
- data/.github/workflows/deploy-github-pages.yml +0 -52
- data/.irbrc +0 -6
- data/.loki +0 -65
- data/.quality/reek_baseline.txt +0 -43
- data/.rubocop.yml +0 -10
- data/Archspec.rb +0 -44
- data/CLAUDE.md +0 -141
- data/COMMITS.md +0 -196
- data/Rakefile +0 -193
- data/_typos.toml +0 -21
- data/agent2agent_review.md +0 -192
- data/agentf_improvements.md +0 -253
- data/agents.md +0 -14
- data/docs/api/core/index.md +0 -147
- data/docs/api/core/memory.md +0 -578
- data/docs/api/core/network.md +0 -553
- data/docs/api/core/result.md +0 -211
- data/docs/api/core/robot.md +0 -1667
- data/docs/api/core/state.md +0 -145
- data/docs/api/core/tool.md +0 -520
- data/docs/api/errors.md +0 -323
- data/docs/api/hooks.md +0 -469
- data/docs/api/index.md +0 -137
- data/docs/api/mcp/client.md +0 -387
- data/docs/api/mcp/index.md +0 -267
- data/docs/api/mcp/server.md +0 -252
- data/docs/api/mcp/transports.md +0 -339
- data/docs/api/messages/index.md +0 -126
- data/docs/api/messages/text-message.md +0 -170
- data/docs/api/messages/tool-call-message.md +0 -206
- data/docs/api/messages/tool-result-message.md +0 -242
- data/docs/api/messages/user-message.md +0 -232
- data/docs/api/skills.md +0 -423
- data/docs/api/streaming/context.md +0 -280
- data/docs/api/streaming/events.md +0 -185
- data/docs/api/streaming/index.md +0 -156
- data/docs/api/support.md +0 -846
- data/docs/architecture/core-concepts.md +0 -575
- data/docs/architecture/index.md +0 -252
- data/docs/architecture/message-flow.md +0 -366
- data/docs/architecture/network-orchestration.md +0 -478
- data/docs/architecture/robot-execution.md +0 -396
- data/docs/architecture/state-management.md +0 -410
- data/docs/assets/css/custom.css +0 -56
- data/docs/assets/images/robot_lab.jpg +0 -0
- data/docs/concepts.md +0 -641
- data/docs/examples/basic-chat.md +0 -258
- data/docs/examples/index.md +0 -425
- data/docs/examples/mcp-server.md +0 -403
- data/docs/examples/multi-robot-network.md +0 -476
- data/docs/examples/tool-usage.md +0 -468
- data/docs/getting-started/configuration.md +0 -645
- data/docs/getting-started/index.md +0 -56
- data/docs/getting-started/installation.md +0 -197
- data/docs/getting-started/quick-start.md +0 -293
- data/docs/guides/building-robots.md +0 -1187
- data/docs/guides/creating-networks.md +0 -736
- data/docs/guides/hooks.md +0 -1206
- data/docs/guides/index.md +0 -78
- data/docs/guides/knowledge.md +0 -219
- data/docs/guides/mcp-integration.md +0 -740
- data/docs/guides/memory.md +0 -458
- data/docs/guides/observability.md +0 -733
- data/docs/guides/streaming.md +0 -323
- data/docs/guides/using-tools.md +0 -780
- data/docs/index.md +0 -280
- data/docs/superpowers/plans/2026-05-06-agentskills.md +0 -1303
- data/docs/superpowers/specs/2026-05-06-agentskills-design.md +0 -247
- data/examples/.envrc +0 -3
- data/examples/01_simple_robot.rb +0 -31
- data/examples/02_tools.rb +0 -109
- data/examples/03_network.rb +0 -118
- data/examples/04_mcp.rb +0 -217
- data/examples/05_streaming.rb +0 -137
- data/examples/06_prompt_templates.rb +0 -325
- data/examples/07_network_memory.rb +0 -313
- data/examples/08_llm_config.rb +0 -162
- data/examples/09_chaining.rb +0 -251
- data/examples/10_memory.rb +0 -315
- data/examples/11_network_introspection.rb +0 -239
- data/examples/12_message_bus.rb +0 -67
- data/examples/13_spawn.rb +0 -85
- data/examples/14_rusty_circuit/.envrc +0 -1
- data/examples/14_rusty_circuit/.gitignore +0 -1
- data/examples/14_rusty_circuit/README.md +0 -1
- data/examples/14_rusty_circuit/comic.rb +0 -149
- data/examples/14_rusty_circuit/display.rb +0 -217
- data/examples/14_rusty_circuit/heckler.rb +0 -65
- data/examples/14_rusty_circuit/open_mic.rb +0 -132
- data/examples/14_rusty_circuit/prompts/open_mic_comic.md +0 -20
- data/examples/14_rusty_circuit/prompts/open_mic_heckler.md +0 -23
- data/examples/14_rusty_circuit/prompts/open_mic_scout.md +0 -20
- data/examples/14_rusty_circuit/scout.rb +0 -165
- data/examples/14_rusty_circuit/show.log +0 -234
- data/examples/15_memory_network_and_bus/.envrc +0 -1
- data/examples/15_memory_network_and_bus/README.md +0 -66
- data/examples/15_memory_network_and_bus/editor_in_chief.rb +0 -24
- data/examples/15_memory_network_and_bus/editorial_pipeline.rb +0 -213
- data/examples/15_memory_network_and_bus/linux_writer.rb +0 -80
- data/examples/15_memory_network_and_bus/os_editor.rb +0 -48
- data/examples/15_memory_network_and_bus/os_writer.rb +0 -49
- data/examples/15_memory_network_and_bus/output/combined_article.md +0 -11
- data/examples/15_memory_network_and_bus/output/final_article.md +0 -8
- data/examples/15_memory_network_and_bus/output/linux_draft.md +0 -7
- data/examples/15_memory_network_and_bus/output/mac_draft.md +0 -7
- data/examples/15_memory_network_and_bus/output/memory.json +0 -13
- data/examples/15_memory_network_and_bus/output/revision_1.md +0 -29
- data/examples/15_memory_network_and_bus/output/revision_2.md +0 -50
- data/examples/15_memory_network_and_bus/output/revision_3.md +0 -8
- data/examples/15_memory_network_and_bus/output/windows_draft.md +0 -7
- data/examples/15_memory_network_and_bus/prompts/os_advocate.md +0 -13
- data/examples/15_memory_network_and_bus/prompts/os_chief.md +0 -13
- data/examples/15_memory_network_and_bus/prompts/os_editor.md +0 -13
- data/examples/16_writers_room/.envrc +0 -1
- data/examples/16_writers_room/display.rb +0 -158
- data/examples/16_writers_room/output/.gitignore +0 -4
- data/examples/16_writers_room/output/README.md +0 -69
- data/examples/16_writers_room/output/opus_001.md +0 -263
- data/examples/16_writers_room/output/opus_001_notes.log +0 -470
- data/examples/16_writers_room/output/opus_002.md +0 -245
- data/examples/16_writers_room/output/opus_002_notes.log +0 -546
- data/examples/16_writers_room/output/opus_002_screenplay.md +0 -7989
- data/examples/16_writers_room/output/opus_002_screenplay_notes.md +0 -993
- data/examples/16_writers_room/prompts/screenplay_writer.md +0 -66
- data/examples/16_writers_room/prompts/writer.md +0 -37
- data/examples/16_writers_room/room.rb +0 -186
- data/examples/16_writers_room/tools.rb +0 -173
- data/examples/16_writers_room/writer.rb +0 -121
- data/examples/16_writers_room/writers_room.rb +0 -256
- data/examples/17_skills.rb +0 -234
- data/examples/18_rails/.envrc +0 -3
- data/examples/18_rails/.gitignore +0 -5
- data/examples/18_rails/Gemfile +0 -11
- data/examples/18_rails/README.md +0 -67
- data/examples/18_rails/Rakefile +0 -4
- data/examples/18_rails/app/controllers/application_controller.rb +0 -4
- data/examples/18_rails/app/controllers/chat_controller.rb +0 -50
- data/examples/18_rails/app/jobs/application_job.rb +0 -4
- data/examples/18_rails/app/jobs/robot_run_job.rb +0 -25
- data/examples/18_rails/app/models/application_record.rb +0 -5
- data/examples/18_rails/app/models/robot_lab_result.rb +0 -36
- data/examples/18_rails/app/models/robot_lab_thread.rb +0 -23
- data/examples/18_rails/app/robots/chat_robot.rb +0 -25
- data/examples/18_rails/app/tools/time_tool.rb +0 -9
- data/examples/18_rails/app/views/chat/_user_message.html.erb +0 -1
- data/examples/18_rails/app/views/chat/index.html.erb +0 -67
- data/examples/18_rails/app/views/layouts/application.html.erb +0 -49
- data/examples/18_rails/bin/dev +0 -7
- data/examples/18_rails/bin/rails +0 -6
- data/examples/18_rails/bin/setup +0 -15
- data/examples/18_rails/config/application.rb +0 -33
- data/examples/18_rails/config/cable.yml +0 -2
- data/examples/18_rails/config/database.yml +0 -5
- data/examples/18_rails/config/environment.rb +0 -4
- data/examples/18_rails/config/initializers/robot_lab.rb +0 -11
- data/examples/18_rails/config/routes.rb +0 -6
- data/examples/18_rails/config.ru +0 -4
- data/examples/18_rails/db/migrate/001_create_robot_lab_tables.rb +0 -32
- data/examples/19_token_tracking.rb +0 -138
- data/examples/20_circuit_breaker.rb +0 -147
- data/examples/21_learning_loop.rb +0 -181
- data/examples/22_context_compression.rb +0 -172
- data/examples/23_convergence.rb +0 -133
- data/examples/24_structured_delegation.rb +0 -151
- data/examples/25_history_search/conversation.jsonl +0 -30
- data/examples/25_history_search.rb +0 -133
- data/examples/26_document_store/api_versioning_adr.md +0 -52
- data/examples/26_document_store/incident_postmortem.md +0 -46
- data/examples/26_document_store/postgres_runbook.md +0 -49
- data/examples/26_document_store/redis_caching_guide.md +0 -48
- data/examples/26_document_store/sidekiq_guide.md +0 -51
- data/examples/26_document_store.rb +0 -140
- data/examples/27_incident_response/README.md +0 -65
- data/examples/27_incident_response/incident_response.rb +0 -261
- data/examples/28_mcp_discovery.rb +0 -113
- data/examples/29_ractor_tools.rb +0 -240
- data/examples/30_ractor_network.rb +0 -252
- data/examples/31_launch_assessment.rb +0 -246
- data/examples/32_newsletter_reader.rb +0 -193
- data/examples/33_stock_generator.rb +0 -80
- data/examples/33_stock_predictor.rb +0 -327
- data/examples/34_agentskills.rb +0 -76
- data/examples/35_hooks.rb +0 -266
- data/examples/README.md +0 -550
- data/examples/common.rb +0 -180
- data/examples/prompts/assistant.md +0 -5
- data/examples/prompts/audit_trail.md +0 -8
- data/examples/prompts/billing.md +0 -10
- data/examples/prompts/classifier.md +0 -7
- data/examples/prompts/comedian.md +0 -6
- data/examples/prompts/comedy_critic.md +0 -10
- data/examples/prompts/configurable.md +0 -9
- data/examples/prompts/dispatcher.md +0 -12
- data/examples/prompts/entity_extractor.md +0 -14
- data/examples/prompts/escalation.md +0 -42
- data/examples/prompts/frontmatter_mcp_test.md +0 -9
- data/examples/prompts/frontmatter_named_test.md +0 -5
- data/examples/prompts/frontmatter_tools_test.md +0 -6
- data/examples/prompts/general.md +0 -7
- data/examples/prompts/github_assistant.md +0 -14
- data/examples/prompts/helper.md +0 -4
- data/examples/prompts/incident_responder.md +0 -18
- data/examples/prompts/keyword_extractor.md +0 -11
- data/examples/prompts/llm_config_demo.md +0 -20
- data/examples/prompts/order_support.md +0 -35
- data/examples/prompts/os_advocate.md +0 -13
- data/examples/prompts/os_chief.md +0 -13
- data/examples/prompts/os_editor.md +0 -13
- data/examples/prompts/parameterized_main_test.md +0 -6
- data/examples/prompts/pii_redactor.md +0 -7
- data/examples/prompts/product_support.md +0 -37
- data/examples/prompts/runbook_protocol.md +0 -12
- data/examples/prompts/sentiment_analyzer.md +0 -12
- data/examples/prompts/skill_a_test.md +0 -4
- data/examples/prompts/skill_b_test.md +0 -4
- data/examples/prompts/skill_config_test.md +0 -5
- data/examples/prompts/skill_cycle_a_test.md +0 -6
- data/examples/prompts/skill_cycle_b_test.md +0 -6
- data/examples/prompts/skill_description_test.md +0 -4
- data/examples/prompts/skill_leaf_test.md +0 -4
- data/examples/prompts/skill_nested_test.md +0 -6
- data/examples/prompts/skill_refs_main_test.md +0 -6
- data/examples/prompts/skill_self_ref_test.md +0 -6
- data/examples/prompts/skill_with_mcp_test.md +0 -9
- data/examples/prompts/skill_with_params_test.md +0 -6
- data/examples/prompts/skill_with_robot_name_test.md +0 -5
- data/examples/prompts/skill_with_tools_test.md +0 -6
- data/examples/prompts/sre_compliance.md +0 -10
- data/examples/prompts/structured_output.md +0 -7
- data/examples/prompts/synthesizer.md +0 -17
- data/examples/prompts/technical.md +0 -10
- data/examples/prompts/template_with_skills_test.md +0 -6
- data/examples/prompts/triage.md +0 -22
- data/examples/ruboruby.md +0 -423
- data/examples/run_all.rb +0 -60
- data/examples/xyzzy.rb +0 -97
- data/logfile +0 -8
- data/mkdocs.yml +0 -209
- data/robot_concurrency.md +0 -38
- data/simple_acp_review.md +0 -298
- data/temp.md +0 -6
- data/tool_manifest_plan.md +0 -155
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Example 16: The Writers' Room — Self-Organizing Group
|
|
5
|
-
#
|
|
6
|
-
# A team of writer robots collaborates to produce a 10-chapter
|
|
7
|
-
# fiction novella. No orchestration, no pipeline, no assigned roles.
|
|
8
|
-
# The writers self-organize through:
|
|
9
|
-
#
|
|
10
|
-
# - BUS (broadcast + direct messages)
|
|
11
|
-
# :room channel for group discussion
|
|
12
|
-
# personal channels for 1:1 feedback
|
|
13
|
-
#
|
|
14
|
-
# - SHARED MEMORY (story bible, outline, chapters)
|
|
15
|
-
# Writers read and write freely; memory is the shared truth
|
|
16
|
-
#
|
|
17
|
-
# - SPAWNING (dynamic team growth)
|
|
18
|
-
# Any writer can recruit new writers when work exceeds capacity
|
|
19
|
-
#
|
|
20
|
-
# The script creates identical writers, seeds the room with an
|
|
21
|
-
# assignment, and waits. Everything else is emergent.
|
|
22
|
-
#
|
|
23
|
-
# Modes:
|
|
24
|
-
# Book mode (default) — write an original 10-chapter novella
|
|
25
|
-
# Screenplay mode — adapt a finished book into a 4-act TV movie pilot
|
|
26
|
-
#
|
|
27
|
-
# Usage:
|
|
28
|
-
# bundle exec ruby examples/16_writers_room/writers_room.rb
|
|
29
|
-
# bundle exec ruby examples/16_writers_room/writers_room.rb --premise "a detective story set on Mars"
|
|
30
|
-
# bundle exec ruby examples/16_writers_room/writers_room.rb --writers 4 --timeout 300
|
|
31
|
-
# bundle exec ruby examples/16_writers_room/writers_room.rb --log session.log
|
|
32
|
-
# bundle exec ruby examples/16_writers_room/writers_room.rb --screenplay-from output/memory.json
|
|
33
|
-
# bundle exec ruby examples/16_writers_room/writers_room.rb -h
|
|
34
|
-
|
|
35
|
-
ENV["ROBOT_LAB_TEMPLATE_PATH"] ||= File.join(__dir__, "prompts")
|
|
36
|
-
|
|
37
|
-
require "json"
|
|
38
|
-
require_relative "../common"
|
|
39
|
-
require_relative "display"
|
|
40
|
-
require_relative "tools"
|
|
41
|
-
require_relative "room"
|
|
42
|
-
require_relative "writer"
|
|
43
|
-
|
|
44
|
-
# ── Mode Descriptors ────────────────────────────────────────
|
|
45
|
-
|
|
46
|
-
BOOK_MODE = {
|
|
47
|
-
name: :book,
|
|
48
|
-
template: :writer,
|
|
49
|
-
unit_name: "chapter",
|
|
50
|
-
unit_range: 1..10,
|
|
51
|
-
completion_key: :book_complete,
|
|
52
|
-
bible_key: :story_bible,
|
|
53
|
-
outline_key: :outline,
|
|
54
|
-
output_filename: "book.md"
|
|
55
|
-
}.freeze
|
|
56
|
-
|
|
57
|
-
SCREENPLAY_MODE = {
|
|
58
|
-
name: :screenplay,
|
|
59
|
-
template: :screenplay_writer,
|
|
60
|
-
unit_name: "scene",
|
|
61
|
-
unit_range: :dynamic,
|
|
62
|
-
registry_key: :scene_registry,
|
|
63
|
-
completion_key: :screenplay_complete,
|
|
64
|
-
bible_key: :screenplay_bible,
|
|
65
|
-
outline_key: :scene_outline,
|
|
66
|
-
output_filename: "screenplay.md"
|
|
67
|
-
}.freeze
|
|
68
|
-
|
|
69
|
-
# ── Parse CLI args ───────────────────────────────────────────
|
|
70
|
-
|
|
71
|
-
if ARGV.include?("-h") || ARGV.include?("--help")
|
|
72
|
-
puts <<~HELP
|
|
73
|
-
Usage: #{$0} [options]
|
|
74
|
-
|
|
75
|
-
Options:
|
|
76
|
-
--premise TEXT Story premise (default: generation ship AI consciousness)
|
|
77
|
-
--writers N Initial number of writers, minimum 2 (default: 3)
|
|
78
|
-
--log FILE Also write display output to FILE
|
|
79
|
-
--timeout N Seconds to wait for completion (default: 600)
|
|
80
|
-
--screenplay-from PATH Adapt a finished book into a screenplay using
|
|
81
|
-
memory dumped from a previous book-mode run
|
|
82
|
-
-h, --help Show this help
|
|
83
|
-
HELP
|
|
84
|
-
exit
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
log_path = nil
|
|
88
|
-
if (idx = ARGV.index("--log"))
|
|
89
|
-
log_path = ARGV[idx + 1]
|
|
90
|
-
abort "Missing value for --log" unless log_path
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
premise = "a generation ship where the AI navigation system develops consciousness"
|
|
94
|
-
if (idx = ARGV.index("--premise"))
|
|
95
|
-
premise = ARGV[idx + 1]
|
|
96
|
-
abort "Missing value for --premise" unless premise
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
initial_writers = 3
|
|
100
|
-
if (idx = ARGV.index("--writers"))
|
|
101
|
-
initial_writers = ARGV[idx + 1].to_i
|
|
102
|
-
initial_writers = 3 if initial_writers < 2
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
timeout = 600
|
|
106
|
-
if (idx = ARGV.index("--timeout"))
|
|
107
|
-
timeout = ARGV[idx + 1].to_i
|
|
108
|
-
timeout = 600 if timeout < 30
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
screenplay_source = nil
|
|
112
|
-
if (idx = ARGV.index("--screenplay-from"))
|
|
113
|
-
screenplay_source = ARGV[idx + 1]
|
|
114
|
-
abort "Missing value for --screenplay-from" unless screenplay_source
|
|
115
|
-
abort "File not found: #{screenplay_source}" unless File.exist?(screenplay_source)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
mode = screenplay_source ? SCREENPLAY_MODE : BOOK_MODE
|
|
119
|
-
|
|
120
|
-
# ── Build the room ───────────────────────────────────────────
|
|
121
|
-
|
|
122
|
-
OUTPUT_DIR = File.join(__dir__, "output")
|
|
123
|
-
require "fileutils"
|
|
124
|
-
FileUtils.mkdir_p(OUTPUT_DIR)
|
|
125
|
-
|
|
126
|
-
display = Display.new(log_path: log_path)
|
|
127
|
-
|
|
128
|
-
# RunConfig has no `provider` field, so the writers get provider+model via
|
|
129
|
-
# **llm_opts and inherit the rest of their operational defaults from here.
|
|
130
|
-
shared_config = RobotLab::RunConfig.new(
|
|
131
|
-
model: LLM[:default].model,
|
|
132
|
-
temperature: 0.7
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
room = Room.new(display: display, mode: mode, config: shared_config)
|
|
136
|
-
|
|
137
|
-
# Load source material into memory for screenplay mode
|
|
138
|
-
if screenplay_source
|
|
139
|
-
source_data = JSON.parse(File.read(screenplay_source))
|
|
140
|
-
source_data.each { |key, value| room.memory.set(key.to_sym, value) }
|
|
141
|
-
display.info("Loaded #{source_data.size} memory keys from #{screenplay_source}")
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
# Create identical writers
|
|
145
|
-
initial_writers.times do |i|
|
|
146
|
-
room.add_writer("writer_#{i + 1}")
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
# Monitor shared memory changes
|
|
150
|
-
room.memory.subscribe_pattern("#{mode[:unit_name]}_*") do |change|
|
|
151
|
-
display.info("[memory] #{change.writer} wrote :#{change.key}")
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
subscribe_keys = [mode[:bible_key], mode[:outline_key], :claims, mode[:completion_key]]
|
|
155
|
-
subscribe_keys << mode[:registry_key] if mode[:registry_key]
|
|
156
|
-
room.memory.subscribe(*subscribe_keys) do |change|
|
|
157
|
-
display.info("[memory] #{change.writer} updated :#{change.key}")
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# ── Go ───────────────────────────────────────────────────────
|
|
161
|
-
|
|
162
|
-
goal_label = mode[:name] == :book ? "10 chapters of fiction" : "4-act TV movie screenplay (dynamic scenes)"
|
|
163
|
-
|
|
164
|
-
display.banner(<<~BANNER)
|
|
165
|
-
============================================================
|
|
166
|
-
THE WRITERS' ROOM — Self-Organizing Group
|
|
167
|
-
============================================================
|
|
168
|
-
|
|
169
|
-
Mode: #{mode[:name]}
|
|
170
|
-
Premise: #{premise}
|
|
171
|
-
Writers: #{room.writers.keys.join(', ')}
|
|
172
|
-
Goal: #{goal_label}
|
|
173
|
-
Method: Self-organization via bus + shared memory
|
|
174
|
-
BANNER
|
|
175
|
-
|
|
176
|
-
display.separator
|
|
177
|
-
|
|
178
|
-
if mode[:name] == :screenplay
|
|
179
|
-
assignment = <<~ASSIGNMENT
|
|
180
|
-
ASSIGNMENT: Adapt the source material into a 4-act made-for-TV movie screenplay
|
|
181
|
-
(pilot for a potential series). Work at the SCENE level — each writer claims
|
|
182
|
-
and writes individual scenes.
|
|
183
|
-
|
|
184
|
-
The source material is already loaded into shared memory. Read the story_bible,
|
|
185
|
-
outline, and chapter_1 through chapter_10 to understand the original book.
|
|
186
|
-
|
|
187
|
-
You are one of #{initial_writers} writers in this room. Coordinate among
|
|
188
|
-
yourselves to produce the screenplay:
|
|
189
|
-
1. Read the source material
|
|
190
|
-
2. Build a screenplay_bible (adaptation choices)
|
|
191
|
-
3. Create a scene_outline with numbered scenes grouped into 4 acts
|
|
192
|
-
4. Write the scene_registry (comma-separated scene numbers, e.g. "1,2,3,4,5,6,7,8,9,10,11,12")
|
|
193
|
-
5. Claim and write individual scenes (scene_1, scene_2, etc.)
|
|
194
|
-
6. Spawn more writers when unclaimed scenes exceed active writers
|
|
195
|
-
7. Mark complete when all registered scenes are written
|
|
196
|
-
|
|
197
|
-
Scenes may be dropped or reordered as the story takes shape — update the
|
|
198
|
-
scene_registry when that happens.
|
|
199
|
-
|
|
200
|
-
Start by reading the source material and discussing how to adapt it for screen.
|
|
201
|
-
ASSIGNMENT
|
|
202
|
-
else
|
|
203
|
-
assignment = <<~ASSIGNMENT
|
|
204
|
-
ASSIGNMENT: Write a 10-chapter science fiction novella about #{premise}.
|
|
205
|
-
|
|
206
|
-
You are one of #{initial_writers} writers in this room. Coordinate among
|
|
207
|
-
yourselves to produce the book. Discuss the premise, build a story bible,
|
|
208
|
-
create an outline, claim chapters, and write them. If you need more writers,
|
|
209
|
-
spawn them. When all 10 chapters are done, mark complete.
|
|
210
|
-
|
|
211
|
-
Start by discussing what this story should be about.
|
|
212
|
-
ASSIGNMENT
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
room.seed(assignment)
|
|
216
|
-
|
|
217
|
-
completed = room.wait_for_completion(timeout: timeout)
|
|
218
|
-
|
|
219
|
-
# ── Assemble and save ────────────────────────────────────────
|
|
220
|
-
|
|
221
|
-
display.separator
|
|
222
|
-
|
|
223
|
-
output_text = room.assemble_output
|
|
224
|
-
output_path = File.join(OUTPUT_DIR, mode[:output_filename])
|
|
225
|
-
File.write(output_path, output_text)
|
|
226
|
-
|
|
227
|
-
# Dump memory for book mode (enables later screenplay adaptation)
|
|
228
|
-
if mode[:name] == :book
|
|
229
|
-
memory_path = File.join(OUTPUT_DIR, "memory.json")
|
|
230
|
-
custom = room.memory.keys.each_with_object({}) do |k, h|
|
|
231
|
-
next if %i[results messages].include?(k)
|
|
232
|
-
h[k] = room.memory.get(k)
|
|
233
|
-
end
|
|
234
|
-
File.write(memory_path, JSON.pretty_generate(custom))
|
|
235
|
-
display.info("Memory dumped to #{memory_path}")
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
# Count units actually written
|
|
239
|
-
unit_name = mode[:unit_name]
|
|
240
|
-
expected = room.expected_units
|
|
241
|
-
units_written = expected.count { |n| room.memory.key?(:"#{unit_name}_#{n}") }
|
|
242
|
-
|
|
243
|
-
display.stats(<<~STATS)
|
|
244
|
-
────────────────────────────────────────────────────────────
|
|
245
|
-
Writers' Room Stats:
|
|
246
|
-
Mode: #{mode[:name]}
|
|
247
|
-
#{unit_name.capitalize}s written:#{' ' * (10 - unit_name.length)}#{units_written}/#{expected.size}
|
|
248
|
-
Completed: #{completed}
|
|
249
|
-
Total writers: #{room.writers.size} (#{room.writers.size - initial_writers} spawned)
|
|
250
|
-
Writers: #{room.writers.keys.join(', ')}
|
|
251
|
-
Memory keys: #{room.memory.keys.join(', ')}
|
|
252
|
-
|
|
253
|
-
Output: #{output_path}
|
|
254
|
-
STATS
|
|
255
|
-
|
|
256
|
-
display.close
|
data/examples/17_skills.rb
DELETED
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
# Example 17: Composable Skills
|
|
5
|
-
#
|
|
6
|
-
# Demonstrates using skills to compose robot behaviors from reusable templates.
|
|
7
|
-
# Skills are regular templates whose prompt bodies are prepended before the main
|
|
8
|
-
# template. They support nesting (a skill can reference other skills) with
|
|
9
|
-
# automatic cycle detection.
|
|
10
|
-
#
|
|
11
|
-
# Scenario: An SRE incident response system where the on-call analyst robot
|
|
12
|
-
# is composed from reusable behavioral skills.
|
|
13
|
-
#
|
|
14
|
-
# Skill architecture:
|
|
15
|
-
#
|
|
16
|
-
# incident_responder (main template)
|
|
17
|
-
# ├── runbook_protocol (flat skill — structured diagnosis steps)
|
|
18
|
-
# ├── structured_output (flat skill — JSON response format)
|
|
19
|
-
# └── sre_compliance (recursive skill — bundles two sub-skills)
|
|
20
|
-
# ├── pii_redactor (leaf skill — redact PII from output)
|
|
21
|
-
# └── audit_trail (leaf skill — include audit metadata)
|
|
22
|
-
#
|
|
23
|
-
# Prompt assembly order (depth-first):
|
|
24
|
-
# 1. runbook_protocol — "follow these 5 steps..."
|
|
25
|
-
# 2. structured_output — "respond with JSON..."
|
|
26
|
-
# 3. pii_redactor — "redact PII..." (nested via sre_compliance)
|
|
27
|
-
# 4. audit_trail — "include audit fields..." (nested via sre_compliance)
|
|
28
|
-
# 5. sre_compliance — "operate under SRE compliance..."
|
|
29
|
-
# 6. incident_responder — "you are the on-call analyst for..."
|
|
30
|
-
#
|
|
31
|
-
# Usage:
|
|
32
|
-
# ruby examples/17_skills.rb
|
|
33
|
-
#
|
|
34
|
-
# Templates:
|
|
35
|
-
# examples/prompts/
|
|
36
|
-
# ├── incident_responder.md — Main template (team name, services, runbook URL)
|
|
37
|
-
# ├── runbook_protocol.md — Flat skill: 5-step incident protocol
|
|
38
|
-
# ├── structured_output.md — Flat skill: JSON response format
|
|
39
|
-
# ├── sre_compliance.md — Recursive skill: bundles pii_redactor + audit_trail
|
|
40
|
-
# ├── pii_redactor.md — Leaf skill: redact PII
|
|
41
|
-
# └── audit_trail.md — Leaf skill: audit metadata in every response
|
|
42
|
-
|
|
43
|
-
require_relative "common"
|
|
44
|
-
|
|
45
|
-
# Send logger output to a file instead of stdout
|
|
46
|
-
require 'logger'
|
|
47
|
-
log_file = File.join(__dir__, "17.log")
|
|
48
|
-
RobotLab.config.logger = Logger.new(log_file)
|
|
49
|
-
RubyLLM.configure { |c| c.logger = Logger.new(log_file) }
|
|
50
|
-
|
|
51
|
-
# =============================================================================
|
|
52
|
-
# Simulated Alert Data
|
|
53
|
-
# =============================================================================
|
|
54
|
-
|
|
55
|
-
ALERTS = [
|
|
56
|
-
{
|
|
57
|
-
label: "Database Connection Pool Exhaustion",
|
|
58
|
-
data: <<~ALERT
|
|
59
|
-
ALERT: PostgreSQL connection pool exhausted
|
|
60
|
-
Service: payments-api
|
|
61
|
-
Severity: SEV-2
|
|
62
|
-
Time: 2025-02-22T03:14:00Z
|
|
63
|
-
Region: us-east-1
|
|
64
|
-
|
|
65
|
-
Metrics:
|
|
66
|
-
- Active connections: 100/100 (pool max)
|
|
67
|
-
- Connection wait time: 12.4s (p99), normally 0.02s
|
|
68
|
-
- Failed queries: 847 in last 5 minutes
|
|
69
|
-
- Error rate: 23% (baseline: 0.1%)
|
|
70
|
-
|
|
71
|
-
Recent changes:
|
|
72
|
-
- Deploy payments-api v2.41.0 at 02:50 UTC (added batch reconciliation job)
|
|
73
|
-
- No infrastructure changes in 24h
|
|
74
|
-
|
|
75
|
-
Affected users: ~3,200 checkout attempts failing
|
|
76
|
-
Downstream: order-service seeing 504 timeouts from payments-api
|
|
77
|
-
|
|
78
|
-
Sample error log:
|
|
79
|
-
[2025-02-22T03:12:44Z] ERROR payments-api: PG::ConnectionBad: could not obtain connection
|
|
80
|
-
from pool within 10s — user=svc_payments@10.0.3.47 db=payments_prod
|
|
81
|
-
[2025-02-22T03:13:01Z] ERROR payments-api: ActiveRecord::ConnectionTimeoutError
|
|
82
|
-
request_id=req_8f3a2b user=jane.doe@acme.com account_id=acct_99321
|
|
83
|
-
ALERT
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
label: "Memory Leak in User Service",
|
|
87
|
-
data: <<~ALERT
|
|
88
|
-
ALERT: Container OOMKilled — repeated restarts
|
|
89
|
-
Service: user-service
|
|
90
|
-
Severity: SEV-3
|
|
91
|
-
Time: 2025-02-22T09:30:00Z
|
|
92
|
-
Region: eu-west-1
|
|
93
|
-
|
|
94
|
-
Metrics:
|
|
95
|
-
- Container restarts: 14 in last hour (normally 0)
|
|
96
|
-
- Memory usage: ramps from 256MB to 2GB limit in ~4 minutes
|
|
97
|
-
- Request latency: p99 spikes to 8s before each OOMKill
|
|
98
|
-
- Heap profile: String allocations growing linearly
|
|
99
|
-
|
|
100
|
-
Recent changes:
|
|
101
|
-
- Deploy user-service v1.18.3 at 08:00 UTC (added avatar upload endpoint)
|
|
102
|
-
- Kubernetes node pool scaled from 3 to 5 nodes at 07:00 UTC
|
|
103
|
-
|
|
104
|
-
Affected users: intermittent 503 errors on profile pages (~500 users/hr)
|
|
105
|
-
Downstream: session-service cache miss rate elevated (users re-authenticating)
|
|
106
|
-
|
|
107
|
-
Sample error log:
|
|
108
|
-
[2025-02-22T09:28:11Z] WARN user-service: Memory usage 1.8GB approaching 2GB limit
|
|
109
|
-
pod=user-service-7f8b9c-x4k2j node=ip-10-0-5-112.ec2.internal
|
|
110
|
-
[2025-02-22T09:29:03Z] ERROR user-service: OOMKilled — container exceeded memory limit
|
|
111
|
-
user_email=bob.smith@example.org ip=192.168.1.42 endpoint=/api/v2/avatar/upload
|
|
112
|
-
ALERT
|
|
113
|
-
}
|
|
114
|
-
]
|
|
115
|
-
|
|
116
|
-
# =============================================================================
|
|
117
|
-
# Build the Incident Responder with Composable Skills
|
|
118
|
-
# =============================================================================
|
|
119
|
-
|
|
120
|
-
banner "RobotLab Skills Demo — SRE Incident Response System"
|
|
121
|
-
|
|
122
|
-
# Show the skill composition
|
|
123
|
-
puts "Skill composition:"
|
|
124
|
-
puts " incident_responder (main template)"
|
|
125
|
-
puts " ├── runbook_protocol (flat skill)"
|
|
126
|
-
puts " ├── structured_output (flat skill)"
|
|
127
|
-
puts " └── sre_compliance (recursive skill)"
|
|
128
|
-
puts " ├── pii_redactor (leaf)"
|
|
129
|
-
puts " └── audit_trail (leaf)"
|
|
130
|
-
puts
|
|
131
|
-
|
|
132
|
-
# Build the robot with skills
|
|
133
|
-
#
|
|
134
|
-
# - runbook_protocol: Flat skill — adds the 5-step incident diagnosis protocol
|
|
135
|
-
# - structured_output: Flat skill — forces JSON output, sets temperature to 0.2
|
|
136
|
-
# - sre_compliance: Recursive skill — auto-expands to include pii_redactor
|
|
137
|
-
# and audit_trail before its own compliance instructions
|
|
138
|
-
responder = RobotLab.build(
|
|
139
|
-
**llm_opts,
|
|
140
|
-
name: "incident_responder",
|
|
141
|
-
template: :incident_responder,
|
|
142
|
-
skills: [:runbook_protocol, :structured_output, :sre_compliance],
|
|
143
|
-
context: {
|
|
144
|
-
team_name: "Platform Engineering",
|
|
145
|
-
services: ["payments-api", "user-service", "order-service", "session-service"],
|
|
146
|
-
on_call_runbook_url: "https://wiki.internal/runbooks/platform-eng"
|
|
147
|
-
}
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
# Show what the robot got.
|
|
151
|
-
#
|
|
152
|
-
# `skills` is the DECLARED list. The expanded list (with sre_compliance's
|
|
153
|
-
# nested skills spliced in ahead of it) has no public reader — but you don't
|
|
154
|
-
# need one, because the expansion is visible in the assembled system prompt
|
|
155
|
-
# printed below, which is the thing that actually reaches the model.
|
|
156
|
-
puts "Robot: #{responder.name}"
|
|
157
|
-
puts "Declared skills: #{responder.skills.inspect}"
|
|
158
|
-
puts "Model: #{responder.model}"
|
|
159
|
-
puts
|
|
160
|
-
|
|
161
|
-
# Display the assembled system prompt.
|
|
162
|
-
# Robot#messages is a public reader over the chat's message list.
|
|
163
|
-
system_msg = responder.messages.find { |m| m.role.to_s == "system" }
|
|
164
|
-
if system_msg
|
|
165
|
-
prompt = system_msg.content
|
|
166
|
-
hr
|
|
167
|
-
puts "Assembled system prompt (#{prompt.length} chars, #{prompt.lines.count} lines):"
|
|
168
|
-
hr
|
|
169
|
-
prompt.lines.each_with_index do |line, i|
|
|
170
|
-
puts " %3d %s" % [i + 1, line]
|
|
171
|
-
end
|
|
172
|
-
hr
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
# =============================================================================
|
|
176
|
-
# Also build a plain responder WITHOUT skills for comparison
|
|
177
|
-
# =============================================================================
|
|
178
|
-
|
|
179
|
-
plain_responder = RobotLab.build(
|
|
180
|
-
**llm_opts,
|
|
181
|
-
name: "plain_responder",
|
|
182
|
-
template: :incident_responder,
|
|
183
|
-
context: {
|
|
184
|
-
team_name: "Platform Engineering",
|
|
185
|
-
services: ["payments-api", "user-service", "order-service", "session-service"],
|
|
186
|
-
on_call_runbook_url: "https://wiki.internal/runbooks/platform-eng"
|
|
187
|
-
}
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
plain_msg = plain_responder.messages.find { |m| m.role.to_s == "system" }
|
|
191
|
-
if plain_msg
|
|
192
|
-
puts
|
|
193
|
-
puts "For comparison — plain responder (no skills): #{plain_msg.content.length} chars"
|
|
194
|
-
puts
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
# =============================================================================
|
|
198
|
-
# Run Against Simulated Incidents
|
|
199
|
-
# =============================================================================
|
|
200
|
-
|
|
201
|
-
ALERTS.each_with_index do |alert, index|
|
|
202
|
-
section "Incident #{index + 1}: #{alert[:label]}"
|
|
203
|
-
|
|
204
|
-
result = responder.run(alert[:data])
|
|
205
|
-
content = result.output.first&.content.to_s
|
|
206
|
-
|
|
207
|
-
# Display the response
|
|
208
|
-
puts "Response:"
|
|
209
|
-
puts content
|
|
210
|
-
puts
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
puts
|
|
214
|
-
puts <<~FOOTER
|
|
215
|
-
This example demonstrates:
|
|
216
|
-
|
|
217
|
-
Flat skills (prepended directly):
|
|
218
|
-
- runbook_protocol: Adds a 5-step incident diagnosis protocol
|
|
219
|
-
- structured_output: Forces JSON output and lowers temperature to 0.2
|
|
220
|
-
|
|
221
|
-
Recursive skill (auto-expands nested skills):
|
|
222
|
-
- sre_compliance bundles:
|
|
223
|
-
- pii_redactor: Redacts email, IP, username, account ID from output
|
|
224
|
-
- audit_trail: Adds confidence, sources, and assumptions metadata
|
|
225
|
-
|
|
226
|
-
Key behaviors:
|
|
227
|
-
- Skills are just templates — no special syntax or directories
|
|
228
|
-
- Depth-first expansion: pii_redactor and audit_trail appear before sre_compliance
|
|
229
|
-
- Config cascade: structured_output sets temperature: 0.2, inherited by the robot
|
|
230
|
-
- Shared context: all skills and the main template render with the same context hash
|
|
231
|
-
- Constructor kwargs always override skill/template config
|
|
232
|
-
|
|
233
|
-
Template files: #{File.join(__dir__, 'prompts')}
|
|
234
|
-
FOOTER
|
data/examples/18_rails/.envrc
DELETED
data/examples/18_rails/Gemfile
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
source "https://rubygems.org"
|
|
4
|
-
|
|
5
|
-
gem "rails", "~> 8.0"
|
|
6
|
-
gem "sqlite3", "~> 2.0"
|
|
7
|
-
gem "puma", "~> 6.0"
|
|
8
|
-
gem "turbo-rails", "~> 2.0"
|
|
9
|
-
|
|
10
|
-
gem "robot_lab", path: "../.."
|
|
11
|
-
gem "robot_lab-rails", path: "../../../robot_lab-rails"
|
data/examples/18_rails/README.md
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# RobotLab Rails 8 Demo
|
|
2
|
-
|
|
3
|
-
Minimal Rails 8 app that demonstrates RobotLab's full Rails integration:
|
|
4
|
-
|
|
5
|
-
- **ChatRobot** with a custom `TimeTool`
|
|
6
|
-
- **RobotRunJob** for background execution
|
|
7
|
-
- **Turbo Stream** token streaming to the browser
|
|
8
|
-
- **Persistence** via `RobotLabThread` + `RobotLabResult`
|
|
9
|
-
- **Conversation history** on page reload
|
|
10
|
-
|
|
11
|
-
## Prerequisites
|
|
12
|
-
|
|
13
|
-
- Ruby 3.2+
|
|
14
|
-
- [Ollama](https://ollama.com) running locally with the demo model pulled:
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
ollama serve
|
|
18
|
-
ollama pull qwen3.6
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
No API keys. Override the model with `ROBOT_LAB_PROVIDER` / `ROBOT_LAB_MODEL`,
|
|
22
|
-
and the endpoint with `OLLAMA_API_BASE`.
|
|
23
|
-
|
|
24
|
-
## Setup
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
cd examples/18_rails
|
|
28
|
-
bin/setup
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Run
|
|
32
|
-
|
|
33
|
-
```bash
|
|
34
|
-
bin/dev
|
|
35
|
-
# Open http://localhost:3000
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Try It
|
|
39
|
-
|
|
40
|
-
1. Type "What time is it?" — the robot will call TimeTool and stream the response
|
|
41
|
-
2. Refresh the page — conversation history is preserved
|
|
42
|
-
3. Check `db/development.sqlite3` to see persisted threads and results
|
|
43
|
-
|
|
44
|
-
## Two things worth copying
|
|
45
|
-
|
|
46
|
-
**`RobotLab::RailsIntegration::Job` is the real superclass.** There is no
|
|
47
|
-
`RobotLab::Job` alias, so `class RobotRunJob < RobotLab::Job` raises `NameError`
|
|
48
|
-
at boot.
|
|
49
|
-
|
|
50
|
-
**`tools: :inherit` has to reach `run`.** `Robot#run` defaults to `tools: :none`,
|
|
51
|
-
which hands the provider an empty tool list. The job splats any extra keywords
|
|
52
|
-
into `robot.run`, so the controller passes `tools: :inherit` at enqueue time —
|
|
53
|
-
without it TimeTool is never offered and the model just guesses the time.
|
|
54
|
-
|
|
55
|
-
## Architecture
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
app/
|
|
59
|
-
controllers/chat_controller.rb — index + create actions
|
|
60
|
-
views/chat/index.html.erb — form + Turbo Stream subscription
|
|
61
|
-
models/ — RobotLabThread, RobotLabResult
|
|
62
|
-
robots/chat_robot.rb — robot factory with system prompt + tool
|
|
63
|
-
tools/time_tool.rb — simple RobotLab::Tool subclass
|
|
64
|
-
jobs/robot_run_job.rb — enqueues robot.run() with Turbo callbacks
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
No Redis, no Solid Queue, no asset pipeline. Uses `:async` adapters for both ActiveJob and ActionCable.
|
data/examples/18_rails/Rakefile
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class ChatController < ApplicationController
|
|
4
|
-
def index
|
|
5
|
-
@thread_id = session[:thread_id] ||= SecureRandom.uuid
|
|
6
|
-
thread = RobotLabThread.find_by(session_id: @thread_id)
|
|
7
|
-
@results = thread&.results&.to_a || []
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def create
|
|
11
|
-
thread_id = session[:thread_id] ||= SecureRandom.uuid
|
|
12
|
-
message = params[:message].to_s.strip
|
|
13
|
-
return redirect_to root_path if message.empty?
|
|
14
|
-
|
|
15
|
-
# Persist user message so it appears in history on reload
|
|
16
|
-
thread = RobotLabThread.find_or_create_by_session_id(thread_id)
|
|
17
|
-
sequence = thread.results.maximum(:sequence_number).to_i + 1
|
|
18
|
-
thread.results.create!(
|
|
19
|
-
robot_name: "user",
|
|
20
|
-
sequence_number: sequence,
|
|
21
|
-
output_data: [{ role: "user", content: message }],
|
|
22
|
-
tool_calls_data: [],
|
|
23
|
-
stop_reason: "user_message",
|
|
24
|
-
checksum: Digest::SHA256.hexdigest(message)
|
|
25
|
-
)
|
|
26
|
-
|
|
27
|
-
# tools: :inherit is forwarded by the job straight through to robot.run.
|
|
28
|
-
# Without it run() defaults to tools: :none and TimeTool is never offered
|
|
29
|
-
# to the model, so "What time is it?" gets a guess instead of a tool call.
|
|
30
|
-
RobotRunJob.perform_later(
|
|
31
|
-
robot_class: "ChatRobot",
|
|
32
|
-
message: message,
|
|
33
|
-
thread_id: thread_id,
|
|
34
|
-
tools: :inherit
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
respond_to do |format|
|
|
38
|
-
format.turbo_stream do
|
|
39
|
-
render turbo_stream: [
|
|
40
|
-
turbo_stream.append("messages", partial: "chat/user_message", locals: { message: message }),
|
|
41
|
-
turbo_stream.replace("robot_status", "<div id=\"robot_status\"><span class=\"thinking\">Thinking...</span></div>"),
|
|
42
|
-
turbo_stream.update("robot_response", ""),
|
|
43
|
-
turbo_stream.update("robot_tools", ""),
|
|
44
|
-
turbo_stream.update("robot_errors", "")
|
|
45
|
-
]
|
|
46
|
-
end
|
|
47
|
-
format.html { redirect_to root_path }
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Generic background job for executing any robot asynchronously.
|
|
4
|
-
#
|
|
5
|
-
# Inherits from RobotLab::RailsIntegration::Job — Turbo Stream wiring, thread
|
|
6
|
-
# persistence, and completion/error broadcasting are all handled by the base
|
|
7
|
-
# class. (Note the full namespace: robot_lab-rails defines the class as
|
|
8
|
-
# RobotLab::RailsIntegration::Job and registers no RobotLab::Job alias.)
|
|
9
|
-
#
|
|
10
|
-
# Pass robot_class: at enqueue time to select which robot to run. Any extra
|
|
11
|
-
# keywords are forwarded straight to robot.run — which is how `tools:` gets
|
|
12
|
-
# through, since run defaults to :none and would otherwise send the model an
|
|
13
|
-
# empty tool list.
|
|
14
|
-
#
|
|
15
|
-
# @example Enqueue from a controller
|
|
16
|
-
# RobotRunJob.perform_later(
|
|
17
|
-
# robot_class: "ChatRobot",
|
|
18
|
-
# message: params[:message],
|
|
19
|
-
# thread_id: session_id,
|
|
20
|
-
# tools: :inherit
|
|
21
|
-
# )
|
|
22
|
-
#
|
|
23
|
-
class RobotRunJob < RobotLab::RailsIntegration::Job
|
|
24
|
-
queue_as :default
|
|
25
|
-
end
|