Class: Ask::Message
- Inherits:
-
Object
- Object
- Ask::Message
- Defined in:
- lib/ask/conversation.rb
Overview
A single message in a conversation. Immutable after creation.
Valid roles are: :system, :user, :assistant, :tool.
Constant Summary collapse
- VALID_ROLES =
%i[system user assistant tool].freeze
Instance Attribute Summary collapse
-
#content ⇒ String?
readonly
Message text content.
-
#content_blocks ⇒ Array<Ask::Content::Text, Ask::Content::Image, ...>?
readonly
Structured content blocks for multi-modal messages.
-
#metadata ⇒ Hash
readonly
Arbitrary metadata attached to this message.
-
#name ⇒ String?
readonly
Optional participant name (for multi-agent scenarios).
-
#role ⇒ Symbol
readonly
Message role (:system, :user, :assistant, :tool).
-
#tool_call_id ⇒ String?
readonly
Tool call ID this message is responding to.
-
#tool_calls ⇒ Array<Hash>?
readonly
Tool calls included in this message.
Class Method Summary collapse
-
.from_h(hash) ⇒ Message
Rebuild a message from a
to_hhash (persistence round-trips).
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
(also: #eql?)
True if role, content/block, name, and tool metadata all match.
-
#assistant? ⇒ Boolean
True if role is :assistant.
- #hash ⇒ Object
-
#initialize(role:, content: nil, name: nil, tool_call_id: nil, tool_calls: nil, metadata: {}) ⇒ Message
constructor
Create a new message.
- #inspect ⇒ String
-
#multimodal? ⇒ Boolean
True if this message contains non-text content blocks (images, audio, video, or files).
- #preview_text(text) ⇒ Object
-
#system? ⇒ Boolean
True if role is :system.
-
#to_h ⇒ Hash
Convert to a hash suitable for provider wire format serialization.
-
#tool? ⇒ Boolean
True if role is :tool.
-
#tool_call? ⇒ Boolean
True if this message contains tool calls.
-
#tool_result? ⇒ Boolean
True if this is a tool result message.
-
#user? ⇒ Boolean
True if role is :user.
Constructor Details
#initialize(role:, content: nil, name: nil, tool_call_id: nil, tool_calls: nil, metadata: {}) ⇒ Message
Create a new message.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/ask/conversation.rb', line 45 def initialize(role:, content: nil, name: nil, tool_call_id: nil, tool_calls: nil, metadata: {}) @role = normalize_role!(role) @name = normalize_name(name) @tool_call_id = tool_call_id @tool_calls = tool_calls @metadata = .dup.freeze if content.is_a?(Array) validate_content_blocks!(content) @content_blocks = content.map(&:freeze).freeze @content = extract_text_from_blocks(@content_blocks) else @content = content @content_blocks = nil end validate! freeze end |
Instance Attribute Details
#content ⇒ String? (readonly)
Returns message text content. For multi-modal messages this is the concatenation of all text blocks. See #content_blocks for the full structured content.
15 16 17 |
# File 'lib/ask/conversation.rb', line 15 def content @content end |
#content_blocks ⇒ Array<Ask::Content::Text, Ask::Content::Image, ...>? (readonly)
Returns structured content blocks for multi-modal messages. nil when the
message was created with a plain string content: (backward compatible).
20 21 22 |
# File 'lib/ask/conversation.rb', line 20 def content_blocks @content_blocks end |
#metadata ⇒ Hash (readonly)
Returns arbitrary metadata attached to this message.
32 33 34 |
# File 'lib/ask/conversation.rb', line 32 def @metadata end |
#name ⇒ String? (readonly)
Returns optional participant name (for multi-agent scenarios).
23 24 25 |
# File 'lib/ask/conversation.rb', line 23 def name @name end |
#role ⇒ Symbol (readonly)
Returns message role (:system, :user, :assistant, :tool).
10 11 12 |
# File 'lib/ask/conversation.rb', line 10 def role @role end |
#tool_call_id ⇒ String? (readonly)
Returns tool call ID this message is responding to.
26 27 28 |
# File 'lib/ask/conversation.rb', line 26 def tool_call_id @tool_call_id end |
#tool_calls ⇒ Array<Hash>? (readonly)
Returns tool calls included in this message.
29 30 31 |
# File 'lib/ask/conversation.rb', line 29 def tool_calls @tool_calls end |
Class Method Details
.from_h(hash) ⇒ Message
Rebuild a message from a to_h hash (persistence round-trips).
Content-block arrays are reconstructed via Content.from_h.
149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/ask/conversation.rb', line 149 def self.from_h(hash) hash = hash.transform_keys(&:to_s) content = hash["content"] content = content.map { |block| Content.from_h(block) } if content.is_a?(Array) new( role: hash["role"].to_sym, content: content, name: hash["name"], tool_call_id: hash["tool_call_id"], tool_calls: hash["tool_calls"], metadata: hash["metadata"] || {} ) end |
Instance Method Details
#==(other) ⇒ Boolean Also known as: eql?
Returns true if role, content/block, name, and tool metadata all match.
165 166 167 168 169 170 171 172 |
# File 'lib/ask/conversation.rb', line 165 def ==(other) return false unless other.is_a?(Message) @role == other.role && @content == other.content && @content_blocks == other.content_blocks && @name == other.name && @tool_call_id == other.tool_call_id && @tool_calls == other.tool_calls end |
#assistant? ⇒ Boolean
Returns true if role is :assistant.
86 |
# File 'lib/ask/conversation.rb', line 86 def assistant? = @role == :assistant |
#hash ⇒ Object
175 176 177 |
# File 'lib/ask/conversation.rb', line 175 def hash [@role, @content, @content_blocks, @name, @tool_call_id, @tool_calls].hash end |
#inspect ⇒ String
180 181 182 183 184 185 186 187 188 189 |
# File 'lib/ask/conversation.rb', line 180 def inspect label = multimodal? ? "multimodal" : (@content_blocks ? "rich" : "text") preview = if @content_blocks blk = @content_blocks.find { |b| b.is_a?(Content::Text) } blk ? preview_text(blk.text) : label elsif @content @content.length > 57 ? "#{@content[0,57]}..." : @content end "#<Ask::Message role=#{@role.inspect} #{label} #{preview.inspect}>" end |
#multimodal? ⇒ Boolean
Returns true if this message contains non-text content blocks (images, audio, video, or files).
67 68 69 70 71 |
# File 'lib/ask/conversation.rb', line 67 def multimodal? return false unless @content_blocks @content_blocks.any? { |b| !b.is_a?(Content::Text) && b.is_a?(Content::Block) } end |
#preview_text(text) ⇒ Object
191 192 193 |
# File 'lib/ask/conversation.rb', line 191 def preview_text(text) text.length > 57 ? "#{text[0, 57]}..." : text end |
#system? ⇒ Boolean
Returns true if role is :system.
80 |
# File 'lib/ask/conversation.rb', line 80 def system? = @role == :system |
#to_h ⇒ Hash
Convert to a hash suitable for provider wire format serialization.
For multi-modal messages, :content is an array of content block hashes.
For plain text messages, :content is a string (backward compatible).
Each content block hash includes a :type key for discrimination.
Providers can use these to build their wire format.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/ask/conversation.rb', line 99 def to_h base = { role: @role } base[:name] = @name if @name base[:tool_call_id] = @tool_call_id if @tool_call_id if @content_blocks base[:content] = @content_blocks.map(&method(:serialize_content_block)) else base[:content] = @content if @content end if @tool_calls base[:tool_calls] = @tool_calls.is_a?(Array) ? @tool_calls : @tool_calls.map do |id_val, tc| tc_id = if tc.respond_to?(:id) tc.id elsif tc.is_a?(Hash) tc[:id] || tc["id"] || id_val else id_val end tc_name = if tc.respond_to?(:name) tc.name elsif tc.is_a?(Hash) tc.dig(:function, :name) || tc.dig("function", "name") || tc[:name] || tc["name"] || id_val else id_val end tc_args = if tc.respond_to?(:arguments) tc.arguments.is_a?(String) ? tc.arguments : JSON.generate(tc.arguments) elsif tc.is_a?(Hash) raw = tc.dig(:function, :arguments) || tc.dig("function", "arguments") || tc[:arguments] || tc["arguments"] || "{}" raw.is_a?(String) ? raw : JSON.generate(raw) else "{}" end { id: tc_id, type: "function", function: { name: tc_name, arguments: tc_args } } end end base end |
#tool? ⇒ Boolean
Returns true if role is :tool.
89 |
# File 'lib/ask/conversation.rb', line 89 def tool? = @role == :tool |
#tool_call? ⇒ Boolean
Returns true if this message contains tool calls.
74 |
# File 'lib/ask/conversation.rb', line 74 def tool_call? = @tool_calls&.any? == true |
#tool_result? ⇒ Boolean
Returns true if this is a tool result message.
77 |
# File 'lib/ask/conversation.rb', line 77 def tool_result? = !@tool_call_id.nil? |
#user? ⇒ Boolean
Returns true if role is :user.
83 |
# File 'lib/ask/conversation.rb', line 83 def user? = @role == :user |