Class: SmartPrompt::Conversation

Inherits:
Object
  • Object
show all
Includes:
APIHandler
Defined in:
lib/smart_prompt/conversation.rb

Constant Summary collapse

MODEL_REQUEST_OPTION_KEYS =
%w[
  max_tokens
  max_completion_tokens
  top_p
  top_k
  response_format
  tool_choice
  parallel_tool_calls
  seed
  stop
].freeze

Constants included from APIHandler

APIHandler::MAX_RETRIES, APIHandler::RETRY_OPTIONS

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(engine, tools = nil) ⇒ Conversation

Returns a new instance of Conversation.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/smart_prompt/conversation.rb', line 23

def initialize(engine, tools = nil)
  SmartPrompt.logger.info "Create Conversation"
  @messages = []
  @engine = engine
  @adapters = engine.adapters
  @llms = engine.llms
  @models = engine.models
  @current_llm_name = nil
  @templates = engine.templates
  @temperature = 0.7
  @current_adapter = engine.current_adapter
  @last_response = nil
  @tools = tools
  @request_options = {}
  @pending_content_parts = []
  @thinking_enabled = nil
end

Instance Attribute Details

#config_fileObject (readonly)

Returns the value of attribute config_file.



20
21
22
# File 'lib/smart_prompt/conversation.rb', line 20

def config_file
  @config_file
end

#last_call_idObject (readonly)

Returns the value of attribute last_call_id.



21
22
23
# File 'lib/smart_prompt/conversation.rb', line 21

def last_call_id
  @last_call_id
end

#last_responseObject (readonly)

Returns the value of attribute last_response.



20
21
22
# File 'lib/smart_prompt/conversation.rb', line 20

def last_response
  @last_response
end

#messagesObject (readonly)

Returns the value of attribute messages.



20
21
22
# File 'lib/smart_prompt/conversation.rb', line 20

def messages
  @messages
end

Instance Method Details

#add_message(msg, with_history = false) ⇒ Object



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

def add_message(msg, with_history = false)
  if with_history
    history_messages << msg
  end
  @messages << msg
end

#audio(source, **metadata) ⇒ Object



128
129
130
131
# File 'lib/smart_prompt/conversation.rb', line 128

def audio(source, **)
  @pending_content_parts << media_part("audio", source, **)
  self
end

#embeddings(length) ⇒ Object



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/smart_prompt/conversation.rb', line 190

def embeddings(length)
  Retriable.retriable(RETRY_OPTIONS) do
    raise ConfigurationError, "No LLM selected" if @current_llm.nil?
    text = ""
    @messages.each do |msg|
      if msg[:role] == "user"
        text = msg[:content]
      end
    end
    @last_response = @current_llm.embeddings(text, @model_name)
    @messages = []
    @messages << { role: "system", content: @sys_msg }
    normalize(@last_response, length)
  end
end

#history_messagesObject



87
88
89
# File 'lib/smart_prompt/conversation.rb', line 87

def history_messages
  @engine.history_messages
end

#image(source, token_budget: nil, **metadata) ⇒ Object



123
124
125
126
# File 'lib/smart_prompt/conversation.rb', line 123

def image(source, token_budget: nil, **)
  @pending_content_parts << media_part("image", source, token_budget: token_budget, **)
  self
end

#model(model_name) ⇒ Object



65
66
67
# File 'lib/smart_prompt/conversation.rb', line 65

def model(model_name)
  @model_name = model_name
end

#multimodal_prompt(parts, with_history: false) ⇒ Object



118
119
120
121
# File 'lib/smart_prompt/conversation.rb', line 118

def multimodal_prompt(parts, with_history: false)
  add_message({ role: "user", content: normalize_content_parts(parts) }, with_history)
  self
end

#normalize(x, length) ⇒ Object



180
181
182
183
184
185
186
187
188
# File 'lib/smart_prompt/conversation.rb', line 180

def normalize(x, length)
  if x.length > length
    x = Numo::NArray.cast(x[0..length - 1])
    norm = Math.sqrt((x * x).sum)
    return (x / norm).to_a
  else
    return x.concat([0] * (x.length - length))
  end
end

#prompt(template_name, params = {}, with_history: false) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/smart_prompt/conversation.rb', line 98

def prompt(template_name, params = {}, with_history: false)
  if template_name.class == Symbol
    template_name = template_name.to_s
    SmartPrompt.logger.info "Use template #{template_name}"
    raise "Template #{template_name} not found" unless @templates.key?(template_name)
    content = @templates[template_name].render(params)
    add_user_content(content, with_history)
    self
  else
    add_user_content(template_name, with_history)
    self
  end
end

#request_options(options = {}) ⇒ Object



73
74
75
76
# File 'lib/smart_prompt/conversation.rb', line 73

def request_options(options = {})
  @request_options.merge!(options || {})
  self
end

#send_msg(params = {}) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/smart_prompt/conversation.rb', line 146

def send_msg(params = {})
  Retriable.retriable(RETRY_OPTIONS) do
    raise ConfigurationError, "No LLM selected" if @current_llm.nil?
    if params[:with_history]
      @last_response = send_llm_request(history_messages, nil)
    else
      @last_response = send_llm_request(@messages, nil)
    end
    if @last_response == ""
      @last_response = @current_llm.last_response
    end
    @messages = []
    @messages << { role: "system", content: @sys_msg }
    @last_response
  end
rescue => e
  return "Failed to call LLM after #{MAX_RETRIES} attempts: #{e.message}"
end

#send_msg_by_stream(params = {}, &proc) ⇒ Object



165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/smart_prompt/conversation.rb', line 165

def send_msg_by_stream(params = {}, &proc)
  Retriable.retriable(RETRY_OPTIONS) do
    raise ConfigurationError, "No LLM selected" if @current_llm.nil?
    if params[:with_history]
      send_llm_request(history_messages, proc)
    else
      send_llm_request(@messages, proc)
    end
    @messages = []
    @messages << { role: "system", content: @sys_msg }
  end
rescue => e
  return "Failed to call LLM after #{MAX_RETRIES} attempts: #{e.message}"
end

#send_msg_onceObject



138
139
140
141
142
143
144
# File 'lib/smart_prompt/conversation.rb', line 138

def send_msg_once
  raise "No LLM selected" if @current_llm.nil?
  @last_response = send_llm_request(@messages, nil)
  @messages = []
  @messages << { role: "system", content: @sys_msg }
  @last_response
end

#sys_msg(message, params) ⇒ Object



112
113
114
115
116
# File 'lib/smart_prompt/conversation.rb', line 112

def sys_msg(message, params)
  @sys_msg = thinking_system_message(message)
  add_message({ role: "system", content: @sys_msg }, params[:with_history])
  self
end

#temperature(temperature) ⇒ Object



69
70
71
# File 'lib/smart_prompt/conversation.rb', line 69

def temperature(temperature)
  @temperature = temperature
end

#thinking(enabled = true) ⇒ Object



78
79
80
81
82
83
84
85
# File 'lib/smart_prompt/conversation.rb', line 78

def thinking(enabled = true)
  @thinking_enabled = enabled
  if @sys_msg
    @sys_msg = thinking_system_message(@sys_msg)
    refresh_system_message(@sys_msg)
  end
  self
end

#use(llm_name) ⇒ Object

Raises:



41
42
43
44
45
46
47
# File 'lib/smart_prompt/conversation.rb', line 41

def use(llm_name)
  llm_name = llm_name.to_s
  raise ConfigurationError, "LLM #{llm_name} not configured" unless @llms.key?(llm_name)
  @current_llm = @llms[llm_name]
  @current_llm_name = llm_name
  self
end

#use_model(model_name) ⇒ Object

Raises:



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/smart_prompt/conversation.rb', line 49

def use_model(model_name)
  model_name = model_name.to_s
  model_config = @models[model_name] || @models[model_name.to_sym]
  raise ConfigurationError, "Model #{model_name} not configured" unless model_config

  llm_name = model_config["use"] || model_config[:use]
  configured_model_name = model_config["model"] || model_config[:model]
  raise ConfigurationError, "Model #{model_name} must define use" if llm_name.nil? || llm_name.empty?
  raise ConfigurationError, "Model #{model_name} must define model" if configured_model_name.nil? || configured_model_name.empty?

  use(llm_name)
  model(configured_model_name)
  merge_model_request_options(model_config)
  self
end

#video(source, fps: nil, max_seconds: nil, **metadata) ⇒ Object



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

def video(source, fps: nil, max_seconds: nil, **)
  @pending_content_parts << media_part("video", source, fps: fps, max_seconds: max_seconds, **)
  self
end