Class: RubyLLM::Modes::Classifiers::Chat

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby_llm/modes/classifiers/chat.rb

Overview

The :chat backend: one structured-output turn on a RubyLLM chat.

The system prompt is the built-in frame (Chat.prompt): the router's instructions, the modes, and the conversation. chat_factory: replaces RubyLLM.chat; it is called with model: and must return a chat. confidence is the model's self-report.

Any object with the same call signature is a valid classifier:

call(message:, history:, modes:, instructions:, inputs:) # => Decision

Constant Summary collapse

FRAME_HEAD =
"You route the latest user message to one of the modes below.\nChoose exactly one. Use the conversation only to understand what the\nlatest message refers to. Do not answer the user.\n".strip
FRAME_TAIL =
"Return the structured selection: mode, confidence from 0 to 1, reason."

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model: nil, chat_factory: nil) ⇒ Chat



28
29
30
31
# File 'lib/ruby_llm/modes/classifiers/chat.rb', line 28

def initialize(model: nil, chat_factory: nil)
  @model = model
  @chat_factory = chat_factory
end

Instance Attribute Details

#chat_factory ⇒ Object (readonly)

Returns the value of attribute chat_factory.



26
27
28
# File 'lib/ruby_llm/modes/classifiers/chat.rb', line 26

def chat_factory
  @chat_factory
end

#model ⇒ Object (readonly)

Returns the value of attribute model.



26
27
28
# File 'lib/ruby_llm/modes/classifiers/chat.rb', line 26

def model
  @model
end

Class Method Details

.prompt(message:, history:, modes:, instructions: nil) ⇒ Object

The system prompt for modes (Registration values) and the normalised history, with the router's instructions between the frame and the modes. The latest message is the user turn, so it is not repeated here.



51
52
53
54
55
56
57
58
# File 'lib/ruby_llm/modes/classifiers/chat.rb', line 51

def self.prompt(message:, history:, modes:, instructions: nil)
  sections = [ FRAME_HEAD ]
  sections << instructions unless instructions.nil? || instructions.empty?
  sections << "Modes:\n#{modes.map { |mode| "- #{mode.name}: #{indent(mode.description)}" }.join("\n")}"
  sections << "Conversation:\n#{history.map { |entry| transcript_line(entry) }.join("\n")}" if history.any?
  sections << FRAME_TAIL
  sections.join("\n\n")
end

.schema_for(modes) ⇒ Object

The selection schema: mode is an enum of the available names.



61
62
63
64
65
66
67
68
# File 'lib/ruby_llm/modes/classifiers/chat.rb', line 61

def self.schema_for(modes)
  names = modes.map(&:name)
  Schematist::Schema.create do
    string :mode, enum: names
    number :confidence, minimum: 0, maximum: 1
    string :reason
  end
end

Instance Method Details

#call(message:, history:, modes:, instructions:, inputs:) ⇒ Object



33
34
35
36
37
38
39
# File 'lib/ruby_llm/modes/classifiers/chat.rb', line 33

def call(message:, history:, modes:, instructions:, inputs:)
  @resolved_model = nil
  text = self.class.prompt(message:, history:, modes:, instructions:)
  chat = build_chat
  response = chat.with_instructions(text).with_schema(self.class.schema_for(modes)).ask(message)
  decision_from(response)
end

#trace ⇒ Object

What ran, for Route#classifier: the resolved model id once a chat was built, else the declared model.



43
44
45
# File 'lib/ruby_llm/modes/classifiers/chat.rb', line 43

def trace
  { with: "chat", model: @resolved_model || model }
end