Class: WritersRoom::Actor

Inherits:
Object
  • Object
show all
Defined in:
lib/writers_room/actor.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(character_info) ⇒ Actor

Initialize an Actor with character information

Parameters:

  • character_info (Hash)

    Character details

Options Hash (character_info):

  • :name (String)

    Character’s name (required)

  • :personality (String)

    Character traits and behaviors

  • :voice_pattern (String)

    How the character speaks

  • :relationships (Hash)

    Current relationship statuses

  • :current_arc (String)

    Where they are in their character arc

  • :sport (String)

    Associated sport/activity

  • :age (Integer)

    Character’s age



35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/writers_room/actor.rb', line 35

def initialize(character_info)
  @character_info = character_info
  @character_name = character_info[:name] || character_info["name"]
  @scene_info = {}
  @conversation_history = []
  @llm = nil
  @running = false

  validate_character_info!
  setup_llm

  debug_me("Actor initialized") { :character_name }
end

Instance Attribute Details

#character_infoObject (readonly)

Returns the value of attribute character_info.



23
24
25
# File 'lib/writers_room/actor.rb', line 23

def character_info
  @character_info
end

#character_nameObject (readonly)

Returns the value of attribute character_name.



23
24
25
# File 'lib/writers_room/actor.rb', line 23

def character_name
  @character_name
end

#conversation_historyObject (readonly)

Returns the value of attribute conversation_history.



23
24
25
# File 'lib/writers_room/actor.rb', line 23

def conversation_history
  @conversation_history
end

#scene_infoObject (readonly)

Returns the value of attribute scene_info.



23
24
25
# File 'lib/writers_room/actor.rb', line 23

def scene_info
  @scene_info
end

Instance Method Details

#generate_dialog(prompt_context: nil) ⇒ String

Generate dialog based on current context

Parameters:

  • prompt_context (String) (defaults to: nil)

    Optional additional context

Returns:

  • (String)

    Generated dialog line



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/writers_room/actor.rb', line 94

def generate_dialog(prompt_context: nil)
  system_prompt = build_system_prompt
  user_prompt = build_user_prompt(prompt_context)

  debug_me("Generating dialog for #{@character_name}") do
    [system_prompt.length, user_prompt.length]
  end

  response = @llm.chat([
                         { role: "system", content: system_prompt },
                         { role: "user", content: user_prompt },
                       ])

  dialog = extract_dialog(response)
  @conversation_history << { speaker: @character_name, line: dialog, timestamp: Time.now }

  dialog
end

#perform(channel: "writers_room:dialog") ⇒ Object

Start the actor listening and responding to messages

Parameters:

  • channel (String) (defaults to: "writers_room:dialog")

    Redis channel to subscribe to



71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/writers_room/actor.rb', line 71

def perform(channel: "writers_room:dialog")
  @running = true

  debug_me("#{@character_name} starting performance on channel: #{channel}")

  # Subscribe to the dialog channel
  subscribe_to_dialog(channel) do |message_data|
    break unless @running

    process_message(message_data)
  end
end

#react_to(message_data) ⇒ Boolean

React to incoming dialog and decide whether to respond

Parameters:

  • message_data (Hash)

    Incoming message data

Returns:

  • (Boolean)

    Whether the actor responded



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/writers_room/actor.rb', line 138

def react_to(message_data)
  # Don't react to own messages
  return false if message_data[:from] == @character_name

  # Add to conversation history
  @conversation_history << {
    speaker: message_data[:from],
    line: message_data[:content],
    timestamp: Time.now,
  }

  # Decide whether to respond based on context
  if should_respond?(message_data)
    debug_me("#{@character_name} deciding to respond to #{message_data[:from]}")

    response = generate_dialog(
      prompt_context: "Responding to #{message_data[:from]}: '#{message_data[:content]}'",
    )

    speak(response)
    return true
  end

  false
end

#set_scene(scene_info) ⇒ Object

Set the current scene information

Parameters:

  • scene_info (Hash)

    Scene details

Options Hash (scene_info):

  • :scene_number (Integer)

    Which scene this is

  • :scene_name (String)

    Name/title of the scene

  • :location (String)

    Where the scene takes place

  • :characters (Array<String>)

    List of characters in scene

  • :objectives (String)

    What this character wants in scene

  • :context (String)

    Additional scene context

  • :week (Integer)

    Which week in the timeline



59
60
61
62
63
64
65
66
# File 'lib/writers_room/actor.rb', line 59

def set_scene(scene_info)
  @scene_info = scene_info
  @conversation_history = [] # Reset history for new scene

  debug_me("Scene set for #{@character_name}") do
    [@scene_info[:scene_name], @scene_info[:scene_number]]
  end
end

#speak(dialog, channel: "writers_room:dialog", emotion: nil, addressing: nil) ⇒ Object

Send dialog to the scene via SmartMessage/Redis

Parameters:

  • dialog (String)

    The dialog to send

  • channel (String) (defaults to: "writers_room:dialog")

    Redis channel to publish to

  • emotion (String) (defaults to: nil)

    Optional emotional tone

  • addressing (String) (defaults to: nil)

    Optional character being addressed



119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/writers_room/actor.rb', line 119

def speak(dialog, channel: "writers_room:dialog", emotion: nil, addressing: nil)
  message = DialogMessage.new(
    from: @character_name,
    content: dialog,
    scene: @scene_info[:scene_number],
    timestamp: Time.now.to_i,
    emotion: emotion,
    addressing: addressing,
  )

  message.publish(channel)

  debug_me("#{@character_name} spoke") { dialog }
end

#stopObject

Stop the actor



85
86
87
88
# File 'lib/writers_room/actor.rb', line 85

def stop
  @running = false
  debug_me("#{@character_name} stopping")
end