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.
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 |
Instance Method Details
#==(other) ⇒ Boolean Also known as: eql?
Returns true if role, content/block, name, and tool metadata all match.
145 146 147 148 149 150 151 152 |
# File 'lib/ask/conversation.rb', line 145 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
155 156 157 |
# File 'lib/ask/conversation.rb', line 155 def hash [@role, @content, @content_blocks, @name, @tool_call_id, @tool_calls].hash end |
#inspect ⇒ String
160 161 162 163 164 165 166 167 168 169 |
# File 'lib/ask/conversation.rb', line 160 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
171 172 173 |
# File 'lib/ask/conversation.rb', line 171 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 |