Class: Mistri::Message
- Inherits:
-
Object
- Object
- Mistri::Message
- Defined in:
- lib/mistri/message.rb
Overview
One message in a conversation, the single shape every provider translates to and from its wire format. Immutable: streaming builds snapshots, sessions replay values, and nothing aliases across threads.
Roles: :system, :user, :assistant, :tool (a tool result linked back by tool_call_id). Assistant messages carry the model and provider that produced them, which is what lets a later turn replay history across models, plus usage and the stop reason.
ui is a tool result's host-only channel: it persists with the message and rides its :tool_result event, but no serializer ever sends it to a model. error is the machine-readable failure on an errored turn (ErrorData shape); error_message stays the human story.
Constant Summary collapse
- ROLES =
%i[system user assistant tool].freeze
Class Method Summary collapse
- .assistant(content: nil, tool_calls: [], **meta) ⇒ Object
- .from_h(hash) ⇒ Object
- .system(content) ⇒ Object
- .tool(content:, tool_call_id:, tool_name: nil, ui: nil) ⇒ Object
- .user(content) ⇒ Object
-
.user_with_images(content, images = []) ⇒ Object
A user turn carrying images alongside optional text.
Instance Method Summary collapse
- #assistant? ⇒ Boolean
-
#initialize(role:, content: nil, tool_call_id: nil, tool_name: nil, model: nil, provider: nil, usage: nil, stop_reason: nil, error_message: nil, ui: nil, error: nil) ⇒ Message
constructor
A new instance of Message.
- #system? ⇒ Boolean
-
#text ⇒ Object
Every Text block joined, or nil when the turn carried no text.
-
#to_h ⇒ Object
A serialization shape, not the member hash: rebuild with .from_h, never with new(**to_h).
- #tool? ⇒ Boolean
- #tool_calls ⇒ Object
- #tool_calls? ⇒ Boolean
- #user? ⇒ Boolean
Constructor Details
#initialize(role:, content: nil, tool_call_id: nil, tool_name: nil, model: nil, provider: nil, usage: nil, stop_reason: nil, error_message: nil, ui: nil, error: nil) ⇒ Message
Returns a new instance of Message.
22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/mistri/message.rb', line 22 def initialize(role:, content: nil, tool_call_id: nil, tool_name: nil, model: nil, provider: nil, usage: nil, stop_reason: nil, error_message: nil, ui: nil, error: nil) role = role.to_sym raise ArgumentError, "unknown role #{role.inspect}" unless ROLES.include?(role) if stop_reason && !StopReason.valid?(stop_reason) raise ArgumentError, "unknown stop reason #{stop_reason.inspect}" end super(role:, content: Content.wrap(content).freeze, tool_call_id:, tool_name:, model:, provider:, usage:, stop_reason:, error_message:, ui:, error:) end |
Class Method Details
.assistant(content: nil, tool_calls: [], **meta) ⇒ Object
49 50 51 |
# File 'lib/mistri/message.rb', line 49 def self.assistant(content: nil, tool_calls: [], **) new(role: :assistant, content: [*Content.wrap(content), *tool_calls], **) end |
.from_h(hash) ⇒ Object
57 58 59 60 61 62 63 64 65 66 |
# File 'lib/mistri/message.rb', line 57 def self.from_h(hash) h = hash.transform_keys(&:to_s) new(role: h.fetch("role").to_sym, content: Array(h["content"]).map { |block| Content.from_h(block) }, tool_call_id: h["tool_call_id"], tool_name: h["tool_name"], model: h["model"], provider: h["provider"]&.to_sym, usage: h["usage"] && Usage.from_h(h["usage"]), stop_reason: h["stop_reason"]&.to_sym, error_message: h["error_message"], ui: h["ui"], error: h["error"]) end |
.system(content) ⇒ Object
35 |
# File 'lib/mistri/message.rb', line 35 def self.system(content) = new(role: :system, content:) |
.tool(content:, tool_call_id:, tool_name: nil, ui: nil) ⇒ Object
53 54 55 |
# File 'lib/mistri/message.rb', line 53 def self.tool(content:, tool_call_id:, tool_name: nil, ui: nil) new(role: :tool, content:, tool_call_id:, tool_name:, ui:) end |
.user(content) ⇒ Object
37 |
# File 'lib/mistri/message.rb', line 37 def self.user(content) = new(role: :user, content:) |
.user_with_images(content, images = []) ⇒ Object
A user turn carrying images alongside optional text.
40 41 42 43 44 45 46 47 |
# File 'lib/mistri/message.rb', line 40 def self.user_with_images(content, images = []) images = Array(images) return user(content) if images.empty? text = content.to_s blocks = text.empty? ? images : [Content::Text.new(text:), *images] new(role: :user, content: blocks) end |
Instance Method Details
#assistant? ⇒ Boolean
70 |
# File 'lib/mistri/message.rb', line 70 def assistant? = role == :assistant |
#system? ⇒ Boolean
68 |
# File 'lib/mistri/message.rb', line 68 def system? = role == :system |
#text ⇒ Object
Every Text block joined, or nil when the turn carried no text.
74 75 76 77 |
# File 'lib/mistri/message.rb', line 74 def text texts = content.grep(Content::Text) texts.empty? ? nil : texts.map(&:text).join end |
#to_h ⇒ Object
A serialization shape, not the member hash: rebuild with .from_h, never with new(**to_h).
85 86 87 88 |
# File 'lib/mistri/message.rb', line 85 def to_h { role:, content: content.map(&:to_h), tool_call_id:, tool_name:, model:, provider:, usage: usage&.to_h, stop_reason:, error_message:, ui:, error: }.compact end |
#tool? ⇒ Boolean
71 |
# File 'lib/mistri/message.rb', line 71 def tool? = role == :tool |
#tool_calls ⇒ Object
79 |
# File 'lib/mistri/message.rb', line 79 def tool_calls = content.grep(ToolCall) |
#tool_calls? ⇒ Boolean
81 |
# File 'lib/mistri/message.rb', line 81 def tool_calls? = content.any?(ToolCall) |
#user? ⇒ Boolean
69 |
# File 'lib/mistri/message.rb', line 69 def user? = role == :user |