Class: Boxcars::Conversation

Inherits:
Object
  • Object
show all
Defined in:
lib/boxcars/conversation.rb

Overview

used to keep track of the conversation

Constant Summary collapse

PEOPLE =
%i[system user assistant history].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(lines: []) ⇒ Conversation

Returns a new instance of Conversation.



10
11
12
13
# File 'lib/boxcars/conversation.rb', line 10

def initialize(lines: [])
  @lines = lines
  check_lines(@lines)
end

Instance Attribute Details

#linesObject (readonly)

Returns the value of attribute lines.



6
7
8
# File 'lib/boxcars/conversation.rb', line 6

def lines
  @lines
end

Instance Method Details

#add_assistant(text) ⇒ Object

add assistant text to the conversation at the end

Parameters:

  • text (String)

    The text to add



38
39
40
# File 'lib/boxcars/conversation.rb', line 38

def add_assistant(text)
  @lines << [:assistant, text]
end

#add_conversation(conversation) ⇒ Object

add a conversation to the conversation



61
62
63
# File 'lib/boxcars/conversation.rb', line 61

def add_conversation(conversation)
  @lines += conversation.lines
end

#add_history(conversation) ⇒ Object

insert converation above history line if it is present

Parameters:



67
68
69
70
71
72
73
74
75
76
# File 'lib/boxcars/conversation.rb', line 67

def add_history(conversation)
  # find the history line
  hi = lines.rindex { |ln| ln[0] == :history }
  return unless hi

  @lines = @lines.dup

  # insert the conversation above the history line
  @lines.insert(hi, *conversation.lines)
end

#add_lines(lines) ⇒ Object

add multiple lines to the conversation



55
56
57
58
# File 'lib/boxcars/conversation.rb', line 55

def add_lines(lines)
  check_lines(lines)
  @lines += lines
end

#add_system(text) ⇒ Object

add system text to the conversation at the end

Parameters:

  • text (String)

    The text to add



50
51
52
# File 'lib/boxcars/conversation.rb', line 50

def add_system(text)
  @lines << [:system, text]
end

#add_user(text) ⇒ Object

add user text to the conversation at the end

Parameters:

  • text (String)

    The text to add



44
45
46
# File 'lib/boxcars/conversation.rb', line 44

def add_user(text)
  @lines << [:user, text]
end

#as_messages(inputs = nil) ⇒ Hash

compute the prompt parameters with input substitutions (used for chatGPT)

Parameters:

  • inputs (Hash) (defaults to: nil)

    The inputs to use for the prompt.

Returns:

  • (Hash)

    The formatted prompt { messages: …}



90
91
92
93
94
95
96
# File 'lib/boxcars/conversation.rb', line 90

def as_messages(inputs = nil)
  { messages: no_history.map { |ln| { role: ln.first, content: cformat(ln.last, inputs) } } }
rescue ::KeyError => e
  first_line = e.message.to_s.split("\n").first
  Boxcars.error "Missing prompt input key: #{first_line}"
  raise KeyError, "Prompt format error: #{first_line}"
end

#as_prompt(inputs: nil, prefixes: default_prefixes, show_roles: false) ⇒ Hash

compute the prompt parameters with input substitutions

Parameters:

  • inputs (Hash) (defaults to: nil)

    The inputs to use for the prompt.

Returns:

  • (Hash)

    The formatted prompt { prompt: “…”}



101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/boxcars/conversation.rb', line 101

def as_prompt(inputs: nil, prefixes: default_prefixes, show_roles: false)
  if show_roles
    lines = no_history.map { |ln| [prefixes[ln[0]], ln[1]] }
    lines.map { |ln| cformat("#{ln.first}#{ln.last}", inputs) }.compact.join("\n\n")
  else
    no_history.map { |ln| cformat(ln.last, inputs) }.compact.join("\n\n")
  end
rescue ::KeyError => e
  first_line = e.message.to_s.split("\n").first
  Boxcars.error "Missing prompt input key: #{first_line}"
  raise KeyError, "Prompt format error: #{first_line}"
end

#cformat(*args) ⇒ Object

special format that replaces lone percent signs with double percent signs



133
134
135
136
# File 'lib/boxcars/conversation.rb', line 133

def cformat(*args)
  args[0] = args[0].dup.gsub(/%(?!<)/, '%%') if args.length > 1
  format(*args)
end

#check_lines(lines) ⇒ Object

check the lines

Raises:



16
17
18
19
20
21
22
23
24
# File 'lib/boxcars/conversation.rb', line 16

def check_lines(lines)
  raise ArgumentError, "Lines must be an array" unless lines.is_a?(Array)

  lines.each do |ln|
    raise ArgumentError, "Conversation item must be a array" unless ln.is_a?(Array)
    raise ArgumentError, "Conversation item must have 2 items, role and text" unless ln.size == 2
    raise ArgumentError, "Conversation item must have a role #{ln} in (#{PEOPLE})" unless PEOPLE.include? ln[0]
  end
end

#default_prefixesObject



138
139
140
# File 'lib/boxcars/conversation.rb', line 138

def default_prefixes
  { system: 'System: ', user: 'User: ', assistant: 'Assistant: ', history: :history }
end

#message_textObject

return just the messages for the conversation



83
84
85
# File 'lib/boxcars/conversation.rb', line 83

def message_text
  lines.map(&:last).join("\n")
end

#no_historyObject



78
79
80
# File 'lib/boxcars/conversation.rb', line 78

def no_history
  @lines.reject { |ln| ln[0] == :history }
end

#process_content(content, inputs) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/boxcars/conversation.rb', line 114

def process_content(content, inputs)
  # If content is a string, treat it as text
  if content.is_a?(String)
    [{ type: "text", text: cformat(content, inputs) }]
  # If content is an array, assume it's already in the new format
  elsif content.is_a?(Array)
    content.map do |item|
      if item[:type] == "text"
        { type: "text", text: cformat(item[:text], inputs) }
      else
        item # Pass through non-text items (like images) without modification
      end
    end
  else
    raise ArgumentError, "Invalid content type: #{content.class}"
  end
end

#to_aArray

Returns The result as a convesation array.

Returns:

  • (Array)

    The result as a convesation array



27
28
29
# File 'lib/boxcars/conversation.rb', line 27

def to_a
  lines
end

#to_sString

Returns A conversation string.

Returns:

  • (String)

    A conversation string



32
33
34
# File 'lib/boxcars/conversation.rb', line 32

def to_s
  lines.map { |ln| "#{ln[0]}: #{ln[1]}" }.join("\n")
end