writers_room 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md ADDED
@@ -0,0 +1,572 @@
1
+ # Writer's Room - AI-Powered Multi-Character Dialog System
2
+
3
+ An experimental Ruby-based system for generating multi-character dialog using independent AI agents. Each character is an autonomous actor process that uses LLMs to generate dialog while maintaining consistent personality and voice.
4
+
5
+ ## Overview
6
+
7
+ The Writer's Room is designed to experiment with AI-driven theatrical dialog. It features:
8
+
9
+ - **6 Distinct Characters**: Marcus, Jamie, Tyler, Alex, Benny, and Zoe from a comedic teen play
10
+ - **8 Detailed Scenes**: Complete scene breakdowns with objectives, beats, and relationship progressions
11
+ - **Independent Actor Processes**: Each character runs as a separate Ruby process
12
+ - **Redis-Based Communication**: Actors communicate via SmartMessage over Redis pub/sub
13
+ - **LLM-Powered Dialog**: Uses RubyLLM with Ollama (gpt-oss model) by default
14
+ - **Director Orchestration**: Manages multiple actors and produces transcripts
15
+ - **Flexible Configuration**: Easy switching between Ollama, OpenAI, Anthropic, or other providers
16
+
17
+ ## Project Structure
18
+
19
+ ```
20
+ writers_room/
21
+ ├── actor.rb # Actor class (executable)
22
+ ├── director.rb # Director orchestration script (executable)
23
+ ├── run_scene_example.sh # Quick start launcher (executable)
24
+ ├── messages/ # SmartMessage subclasses
25
+ │ ├── dialog_message.rb
26
+ │ ├── scene_control_message.rb
27
+ │ ├── stage_direction_message.rb
28
+ │ └── meta_message.rb
29
+ ├── projects/ # Project-based organization
30
+ │ └── teen_play/ # Teen comedy play project
31
+ │ ├── project.yml # Project metadata (title, tagline, description)
32
+ │ ├── characters/ # Character YAML definitions
33
+ │ │ ├── marcus.yml
34
+ │ │ ├── jamie.yml
35
+ │ │ ├── tyler.yml
36
+ │ │ ├── alex.yml
37
+ │ │ ├── benny.yml
38
+ │ │ └── zoe.yml
39
+ │ └── scenes/ # Scene YAML definitions
40
+ │ ├── scene_01_gym_wars.yml
41
+ │ ├── scene_02_statistical_anomaly.yml
42
+ │ ├── scene_04_equipment_room.yml
43
+ │ └── scene_08_data_dump.yml
44
+ └── logs/ # Actor process logs (created automatically)
45
+ ```
46
+
47
+ **Note**: The project structure allows multiple independent projects. Character directories are auto-detected from scene file paths.
48
+
49
+ ## Requirements
50
+
51
+ ### System Requirements
52
+ - Ruby 3.0+
53
+ - Redis server running locally or accessible
54
+ - MacStudio M2max (or similar)
55
+
56
+ ### Ruby Gems
57
+ ```bash
58
+ gem install debug_me
59
+ gem install ruby_llm
60
+ gem install smart_message
61
+ gem install redis
62
+ ```
63
+
64
+ ## Setup
65
+
66
+ ### 1. Start Redis Server
67
+
68
+ Ensure Redis is running:
69
+ ```bash
70
+ redis-server
71
+ ```
72
+
73
+ Or if using Homebrew:
74
+ ```bash
75
+ brew services start redis
76
+ ```
77
+
78
+ ### 2. Configure LLM Provider
79
+
80
+ **Default Configuration: Ollama with gpt-oss model**
81
+
82
+ The system is pre-configured to use Ollama with the `gpt-oss` model. Ensure Ollama is running:
83
+
84
+ ```bash
85
+ # Start Ollama
86
+ ollama serve
87
+
88
+ # Pull the gpt-oss model if you haven't already
89
+ ollama pull gpt-oss
90
+ ```
91
+
92
+ **Using Different Providers (Optional)**
93
+
94
+ You can override the default Ollama configuration with environment variables:
95
+
96
+ ```bash
97
+ # Use a different Ollama model
98
+ export RUBY_LLM_MODEL="llama2"
99
+
100
+ # Use Ollama on a different host
101
+ export OLLAMA_URL="http://192.168.1.100:11434"
102
+
103
+ # Switch to OpenAI
104
+ export RUBY_LLM_PROVIDER="openai"
105
+ export OPENAI_API_KEY="your-key-here"
106
+
107
+ # Switch to Anthropic
108
+ export RUBY_LLM_PROVIDER="anthropic"
109
+ export ANTHROPIC_API_KEY="your-key-here"
110
+ ```
111
+
112
+ ### 3. Verify Setup
113
+
114
+ Test that Redis is accessible:
115
+ ```bash
116
+ redis-cli ping
117
+ # Should return: PONG
118
+ ```
119
+
120
+ Test that Ollama is accessible:
121
+ ```bash
122
+ curl http://localhost:11434
123
+ # Should return Ollama version info
124
+
125
+ ollama list | grep gpt-oss
126
+ # Should show the gpt-oss model
127
+ ```
128
+
129
+ ### 4. Configuration Reference
130
+
131
+ For detailed configuration options, see **[CONFIGURATION.md](CONFIGURATION.md)**
132
+
133
+ Quick reference:
134
+ - Default provider: Ollama with gpt-oss model
135
+ - Switch models: `export RUBY_LLM_MODEL="llama2"`
136
+ - Switch providers: `export RUBY_LLM_PROVIDER="openai"`
137
+ - Debug mode: `export DEBUG_ME=1`
138
+
139
+ ## Usage
140
+
141
+ ### Running a Scene
142
+
143
+ The simplest way to run a scene is using the director:
144
+
145
+ ```bash
146
+ ./director.rb -s projects/teen_play/scenes/scene_01_gym_wars.yml
147
+ ```
148
+
149
+ This will:
150
+ 1. Load the scene configuration
151
+ 2. Auto-detect the character directory (`projects/teen_play/characters/`)
152
+ 3. Start actor processes for all characters in the scene
153
+ 4. Monitor and display their dialog in real-time
154
+ 5. Save a transcript when complete
155
+
156
+ ### Director Options
157
+
158
+ ```bash
159
+ ./director.rb [options]
160
+
161
+ Options:
162
+ -s, --scene FILE Scene YAML file (required)
163
+ -c, --characters DIR Character directory (auto-detected if not specified)
164
+ -o, --output FILE Transcript output file
165
+ -l, --max-lines N Maximum lines before ending (default: 50)
166
+ -h, --help Show help
167
+ ```
168
+
169
+ ### Examples
170
+
171
+ **Run Scene 1 with default settings:**
172
+ ```bash
173
+ ./director.rb -s projects/teen_play/scenes/scene_01_gym_wars.yml
174
+ ```
175
+
176
+ **Run Scene 2 with custom transcript name:**
177
+ ```bash
178
+ ./director.rb -s projects/teen_play/scenes/scene_02_statistical_anomaly.yml -o scene2_take1.txt
179
+ ```
180
+
181
+ **Run Scene 4 with more lines:**
182
+ ```bash
183
+ ./director.rb -s projects/teen_play/scenes/scene_04_equipment_room.yml -l 100
184
+ ```
185
+
186
+ ### Running Individual Actors
187
+
188
+ You can also run actors manually for testing:
189
+
190
+ ```bash
191
+ ./actor.rb -c projects/teen_play/characters/marcus.yml -s projects/teen_play/scenes/scene_01_gym_wars.yml
192
+ ```
193
+
194
+ In a separate terminal, run another actor:
195
+ ```bash
196
+ ./actor.rb -c projects/teen_play/characters/jamie.yml -s projects/teen_play/scenes/scene_01_gym_wars.yml
197
+ ```
198
+
199
+ They will automatically begin conversing via Redis.
200
+
201
+ ## Character Definitions
202
+
203
+ Each character is defined in a YAML file with the following structure:
204
+
205
+ ```yaml
206
+ name: Marcus
207
+ age: 16
208
+ personality: |
209
+ Description of character traits and behaviors...
210
+
211
+ voice_pattern: |
212
+ How the character speaks, example phrases...
213
+
214
+ sport: Basketball team statistician
215
+
216
+ relationships:
217
+ Jamie: "Current relationship status..."
218
+ Tyler: "Current relationship status..."
219
+ # ... other characters
220
+
221
+ current_arc: |
222
+ Where the character is in their development...
223
+ ```
224
+
225
+ See `projects/teen_play/characters/` directory for complete examples.
226
+
227
+ ## Scene Definitions
228
+
229
+ Scenes are defined in YAML with detailed structure:
230
+
231
+ ```yaml
232
+ scene_number: 1
233
+ scene_name: "The Gym Wars"
234
+ week: 1
235
+ location: "School gymnasium"
236
+
237
+ context: |
238
+ Detailed scene setup and situation...
239
+
240
+ characters:
241
+ - Marcus
242
+ - Jamie
243
+ - Tyler
244
+ - Alex
245
+ - Benny
246
+ - Zoe
247
+
248
+ scene_objectives:
249
+ Marcus: |
250
+ What Marcus wants to achieve in this scene...
251
+ Jamie: |
252
+ What Jamie wants to achieve in this scene...
253
+ # ... objectives for all characters
254
+
255
+ beat_structure:
256
+ - beat: "The Standoff"
257
+ duration: "2 minutes"
258
+ description: "What happens in this beat..."
259
+
260
+ - beat: "The Negotiators"
261
+ duration: "3 minutes"
262
+ description: "What happens in this beat..."
263
+
264
+ relationship_status:
265
+ Marcus_Jamie: "Strangers → Intrigued"
266
+ Tyler_Alex: "Rivals → Respectful competitors"
267
+ ```
268
+
269
+ See `projects/teen_play/scenes/` directory for complete examples.
270
+
271
+ ## How It Works
272
+
273
+ ### Architecture
274
+
275
+ ```
276
+ ┌─────────────────────────────────────────────────────────┐
277
+ │ DIRECTOR │
278
+ │ - Spawns actor processes │
279
+ │ - Monitors dialog via Redis │
280
+ │ - Saves transcripts │
281
+ └─────────────────┬───────────────────────────────────────┘
282
+
283
+ ┌────────┴────────┐
284
+ │ │
285
+ Redis Pub/Sub Redis Pub/Sub
286
+ │ │
287
+ ┌────┴────┐ ┌────┴────┐
288
+ │ Actor │ │ Actor │
289
+ │ Process │←─────→│ Process │
290
+ │ (Marcus)│ │ (Jamie) │
291
+ └─────────┘ └─────────┘
292
+ │ │
293
+ RubyLLM RubyLLM
294
+ │ │
295
+ LLM Provider LLM Provider
296
+ ```
297
+
298
+ ### Message Flow
299
+
300
+ 1. **Director** sends `SceneControlMessage` to start scene
301
+ 2. **Actors** subscribe to `writers_room:dialog` channel
302
+ 3. **Actor** generates dialog using character info + scene context + conversation history
303
+ 4. **Actor** publishes `DialogMessage` to Redis
304
+ 5. **Other Actors** receive message, decide whether to respond
305
+ 6. **Actor** generates response if appropriate
306
+ 7. **Director** monitors all messages and records transcript
307
+
308
+ ### Dialog Generation
309
+
310
+ Each actor uses a two-part prompt system:
311
+
312
+ **System Prompt:**
313
+ - Character profile (personality, voice pattern, age, sport)
314
+ - Current character arc
315
+ - Relationship statuses
316
+ - Scene context and objectives
317
+ - Instructions for staying in character
318
+
319
+ **User Prompt:**
320
+ - Recent conversation history (last 10 exchanges)
321
+ - Additional context (if responding to specific dialog)
322
+ - Prompt: "What does [Character] say?"
323
+
324
+ The LLM generates a response, which is cleaned and published as dialog.
325
+
326
+ ### Response Decision Logic
327
+
328
+ Actors decide whether to respond based on:
329
+ 1. **Direct address**: Name mentioned in dialog
330
+ 2. **Conversation flow**: Not spoken recently, appropriate turn
331
+ 3. **Random interjection**: 10% chance to interject
332
+ 4. **Character-specific logic**: Can be customized per character
333
+
334
+ ## SmartMessage Integration
335
+
336
+ The system uses SmartMessage subclasses for type-safe Redis communication:
337
+
338
+ ### DialogMessage
339
+ ```ruby
340
+ DialogMessage.new(
341
+ from: "Marcus",
342
+ content: "There's a 73% chance this will work!",
343
+ scene: 1,
344
+ timestamp: Time.now.to_i,
345
+ emotion: "excited", # optional
346
+ addressing: "Jamie" # optional
347
+ )
348
+ ```
349
+
350
+ ### SceneControlMessage
351
+ ```ruby
352
+ SceneControlMessage.start_scene(1)
353
+ SceneControlMessage.stop_scene(1)
354
+ SceneControlMessage.end_scene(1)
355
+ ```
356
+
357
+ ### StageDirectionMessage
358
+ ```ruby
359
+ StageDirectionMessage.new(
360
+ character: "Marcus",
361
+ action: "pulls out tablet nervously",
362
+ scene: 1,
363
+ timestamp: Time.now.to_i
364
+ )
365
+ ```
366
+
367
+ ## Output
368
+
369
+ ### Transcript Format
370
+
371
+ ```
372
+ SCENE 1: The Gym Wars
373
+ Location: Riverside High gymnasium
374
+ Week: 1
375
+
376
+ ------------------------------------------------------------
377
+
378
+ Tyler: We're here until 6:30.
379
+ Alex: So are we. Guess we're roommates.
380
+ Marcus: According to the scheduling system, there's been an error...
381
+ Jamie: Let me see that code. Oh, I see the bug!
382
+ Benny: Can we just settle this with rock-paper-scissors?
383
+ Zoe: As Shakespeare once said... actually, this is more West Side Story!
384
+
385
+ [continues...]
386
+ ```
387
+
388
+ ### Statistics Output
389
+
390
+ ```
391
+ ============================================================
392
+ SCENE STATISTICS
393
+ ============================================================
394
+ Total lines: 47
395
+
396
+ Lines by character:
397
+ Marcus: 12
398
+ Jamie: 11
399
+ Tyler: 9
400
+ Alex: 8
401
+ Benny: 4
402
+ Zoe: 3
403
+ ============================================================
404
+ ```
405
+
406
+ ## Debugging
407
+
408
+ ### Enable Debug Output
409
+
410
+ The system uses the `debug_me` gem. To see debug output:
411
+
412
+ ```bash
413
+ DEBUG_ME=1 ./director.rb -s projects/teen_play/scenes/scene_01_gym_wars.yml
414
+ ```
415
+
416
+ ### Actor Logs
417
+
418
+ Individual actor logs are saved in the `logs/` directory:
419
+ - `logs/marcus_[timestamp].log` - Standard output
420
+ - `logs/marcus_[timestamp]_err.log` - Error output
421
+
422
+ ### Monitor Redis
423
+
424
+ Watch Redis traffic in real-time:
425
+ ```bash
426
+ redis-cli monitor
427
+ ```
428
+
429
+ Or subscribe to the dialog channel:
430
+ ```bash
431
+ redis-cli
432
+ > SUBSCRIBE writers_room:dialog
433
+ ```
434
+
435
+ ## Customization
436
+
437
+ ### Adding New Characters
438
+
439
+ 1. Create a new YAML file in `projects/teen_play/characters/` (or your own project):
440
+ ```yaml
441
+ name: NewCharacter
442
+ age: 16
443
+ personality: |
444
+ Character description...
445
+ voice_pattern: |
446
+ How they speak...
447
+ # ... etc
448
+ ```
449
+
450
+ 2. Add to scene's character list
451
+ 3. Run the scene
452
+
453
+ ### Adding New Scenes
454
+
455
+ 1. Create scene YAML in `projects/teen_play/scenes/` (or your own project):
456
+ ```yaml
457
+ scene_number: 9
458
+ scene_name: "New Scene"
459
+ characters:
460
+ - Character1
461
+ - Character2
462
+ scene_objectives:
463
+ Character1: |
464
+ Objective...
465
+ # ... etc
466
+ ```
467
+
468
+ 2. Run with director:
469
+ ```bash
470
+ ./director.rb -s projects/teen_play/scenes/scene_09_new_scene.yml
471
+ ```
472
+
473
+ The director will auto-detect the character directory from the scene path.
474
+
475
+ ### Customizing LLM Behavior
476
+
477
+ Edit the prompt building methods in `actor.rb`:
478
+ - `build_system_prompt` - Character definition and instructions
479
+ - `build_user_prompt` - Conversation context
480
+
481
+ ### Adjusting Response Logic
482
+
483
+ Modify `should_respond?` method in `actor.rb` to change when actors speak.
484
+
485
+ ## The Play: Character Arcs
486
+
487
+ The complete play spans 8 scenes over 16 weeks:
488
+
489
+ ### Three Couples
490
+
491
+ **Marcus & Jamie** - "The Analysts"
492
+ - Arc: From "love is logical" → "love is beyond logic"
493
+ - Connection: Intellectual equals who make each other braver
494
+
495
+ **Tyler & Alex** - "The Captains"
496
+ - Arc: From rivals → teammates in life
497
+ - Connection: Competitive but supportive, bring out vulnerability
498
+
499
+ **Benny & Zoe** - "The Performers"
500
+ - Arc: From hiding behind personas → authentic selves
501
+ - Connection: See through each other's acts, permission to be real
502
+
503
+ ### Timeline
504
+
505
+ - **Week 1** (Scene 1): Everyone meets
506
+ - **Week 2** (Scene 2): Marcus/Jamie bond over algorithm
507
+ - **Week 4** (Scene 3): Tyler/Alex forced to work together
508
+ - **Week 6** (Scene 4): Benny/Zoe breakthrough in equipment room
509
+ - **Week 8** (Scene 5): Group chat catastrophe, all feelings revealed
510
+ - **Week 10** (Scene 6): Track meet, relationships solidify
511
+ - **Week 14** (Scene 7): Championship games, Tyler/Alex first kiss
512
+ - **Week 16** (Scene 8): Algorithm revealed wrong, love wins
513
+
514
+ See the detailed planning documents for complete scene breakdowns, character maps, and relationship timelines.
515
+
516
+ ## Troubleshooting
517
+
518
+ ### Redis Connection Issues
519
+ ```bash
520
+ # Check Redis is running
521
+ redis-cli ping
522
+
523
+ # Check Redis logs
524
+ tail -f /usr/local/var/log/redis.log
525
+ ```
526
+
527
+ ### Actor Process Issues
528
+ ```bash
529
+ # Check running actor processes
530
+ ps aux | grep actor.rb
531
+
532
+ # Kill hung processes
533
+ pkill -f actor.rb
534
+ ```
535
+
536
+ ### LLM API Issues
537
+ - Verify API keys are set correctly
538
+ - Check rate limits on your LLM provider
539
+ - Review actor logs in `logs/` directory
540
+
541
+ ## Performance Tips
542
+
543
+ 1. **Limit dialog exchanges** with `-l` flag to prevent runaway conversations
544
+ 2. **Use faster models** for experimentation (e.g., GPT-3.5, Claude Haiku)
545
+ 3. **Run fewer actors** initially to test scene dynamics
546
+ 4. **Monitor Redis memory** with `redis-cli info memory`
547
+
548
+ ## Future Enhancements
549
+
550
+ Potential areas for expansion:
551
+
552
+ - [ ] Stage direction generation
553
+ - [ ] Emotion detection and tracking
554
+ - [ ] Dynamic scene beat progression
555
+ - [ ] Multi-scene continuity
556
+ - [ ] Voice synthesis integration
557
+ - [ ] Visual character representation
558
+ - [ ] Real-time web interface
559
+ - [ ] Character memory/learning across scenes
560
+ - [ ] Playwright format export
561
+
562
+ ## License
563
+
564
+ Experimental project for educational purposes.
565
+
566
+ ## Credits
567
+
568
+ Created for exploring multi-agent AI dialog generation using Ruby, RubyLLM, and SmartMessage.
569
+
570
+ ---
571
+
572
+ **Happy Writing!** 🎭
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: :test
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "writers_room"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/bin/wr ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Add lib directory to load path for development
5
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
6
+
7
+ require "writers_room/cli"
8
+
9
+ WritersRoom::CLI.start(ARGV)