Skip to content
Kward Search API index

Module: Kward::CLITranscriptFormatter

Defined in:
lib/kward/cli/transcript_formatter.rb

Overview

Formats conversation messages for terminal transcript display.

Class Method Summary collapse

Class Method Details

.assistant_content_text(message) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/kward/cli/transcript_formatter.rb', line 56

def assistant_content_text(message)
  text = content_text(MessageAccess.content(message))
  return text unless text.empty?

  response_items(message).filter_map do |item|
    next unless MessageAccess.value(item, :type) == "message"
    next if MessageAccess.value(item, :phase).to_s == "commentary"

    response_item_text(MessageAccess.value(item, :content))
  end.join
end

.content_part_text(part) ⇒ Object



113
114
115
116
117
118
119
120
121
122
# File 'lib/kward/cli/transcript_formatter.rb', line 113

def content_part_text(part)
  type = MessageAccess.value(part, :type)
  if type == "text"
    MessageAccess.value(part, :text)
  elsif type == "image"
    path = MessageAccess.value(part, :path)
    media_type = MessageAccess.value(part, :media_type) || MessageAccess.value(part, :mimeType) || "image"
    "[#{media_type}#{path ? ": #{path}" : ""}]"
  end
end

.content_text(content) ⇒ Object



47
48
49
50
51
52
53
54
# File 'lib/kward/cli/transcript_formatter.rb', line 47

def content_text(content)
  case content
  when Array
    content.filter_map { |part| content_part_text(part) }.join("\n")
  else
    content.to_s
  end
end

.decoded_image_size(data) ⇒ Object



154
155
156
157
158
159
160
# File 'lib/kward/cli/transcript_formatter.rb', line 154

def decoded_image_size(data)
  return nil if data.to_s.empty?

  Base64.decode64(data.to_s.gsub(/\s+/, "")).bytesize
rescue ArgumentError
  nil
end

.full_text(message) ⇒ Object



109
110
111
# File 'lib/kward/cli/transcript_formatter.rb', line 109

def full_text(message)
  MessageText.full_text(message)
end

.image_part_reference(part) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/kward/cli/transcript_formatter.rb', line 140

def image_part_reference(part)
  data = MessageAccess.value(part, :data)
  path = MessageAccess.value(part, :path)
  media_type = MessageAccess.value(part, :media_type) || MessageAccess.value(part, :mimeType) || "image"
  {
    status: :attached,
    type: "image",
    label: path.to_s.empty? ? "pasted image" : File.basename(path),
    media_type: media_type,
    size_bytes: decoded_image_size(data),
    path: path
  }
end

.image_parts(message) ⇒ Object



90
91
92
93
94
95
# File 'lib/kward/cli/transcript_formatter.rb', line 90

def image_parts(message)
  content = MessageAccess.content(message)
  return [] unless content.is_a?(Array)

  content.select { |part| MessageAccess.value(part, :type) == "image" }
end

.image_references(message) ⇒ Object



97
98
99
# File 'lib/kward/cli/transcript_formatter.rb', line 97

def image_references(message)
  image_parts(message).map { |part| image_part_reference(part) }
end

.reasoning(message) ⇒ Object



12
13
14
# File 'lib/kward/cli/transcript_formatter.rb', line 12

def reasoning(message)
  reasoning_blocks(message).join("\n")
end

.reasoning_blocks(message) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/kward/cli/transcript_formatter.rb', line 16

def reasoning_blocks(message)
  response_blocks = response_items(message).flat_map do |item|
    case MessageAccess.value(item, :type)
    when "reasoning"
      summary = response_item_text_parts(MessageAccess.value(item, :summary))
      summary.empty? ? response_item_text_parts(MessageAccess.value(item, :content)) : summary
    when "message"
      next [] unless MessageAccess.value(item, :phase).to_s == "commentary"

      response_item_text_parts(MessageAccess.value(item, :content))
    else
      []
    end
  end
  return response_blocks unless response_blocks.empty?

  content = MessageAccess.content(message)
  if content.is_a?(Array)
    blocks = content.filter_map do |part|
      type = MessageAccess.value(part, :type)
      next unless ["thinking", "reasoning"].include?(type)

      MessageAccess.value(part, :thinking) || MessageAccess.value(part, :reasoning) || MessageAccess.value(part, :text)
    end.map(&:to_s).reject(&:empty?)
    return blocks unless blocks.empty?
  end

  direct = MessageAccess.value(message, :reasoning_summary).to_s
  direct.empty? ? [] : [direct]
end

.response_item_text(parts) ⇒ Object



128
129
130
# File 'lib/kward/cli/transcript_formatter.rb', line 128

def response_item_text(parts)
  response_item_text_parts(parts).join
end

.response_item_text_parts(parts) ⇒ Object



132
133
134
135
136
137
138
# File 'lib/kward/cli/transcript_formatter.rb', line 132

def response_item_text_parts(parts)
  Array(parts).filter_map do |part|
    next unless part.is_a?(Hash)

    MessageAccess.value(part, :text) || MessageAccess.value(part, :refusal)
  end.map(&:to_s).reject(&:empty?)
end

.response_items(message) ⇒ Object



124
125
126
# File 'lib/kward/cli/transcript_formatter.rb', line 124

def response_items(message)
  MessageAccess.response_items(message)
end

.synthetic_tool_call(name, id) ⇒ Object



101
102
103
104
105
106
107
# File 'lib/kward/cli/transcript_formatter.rb', line 101

def synthetic_tool_call(name, id)
  {
    "id" => id || "restored_tool",
    "type" => "function",
    "function" => { "name" => name || "tool", "arguments" => "{}" }
  }
end

.user_display_text(message) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/kward/cli/transcript_formatter.rb', line 68

def user_display_text(message)
  display_content = MessageAccess.display_content(message)
  return display_content.to_s unless display_content.nil?

  content = MessageAccess.content(message)
  return content.to_s unless content.is_a?(Array)

  text = content.filter_map do |part|
    next unless MessageAccess.value(part, :type) == "text"

    MessageAccess.value(part, :text)
  end.join("\n")
  ImageAttachments.display_text_without_references(text, ImageAttachments.references_from_text(text).select { |reference| reference[:status] == :attached })
end

.user_transcript_input(message) ⇒ Object



83
84
85
86
87
88
# File 'lib/kward/cli/transcript_formatter.rb', line 83

def user_transcript_input(message)
  content = MessageAccess.content(message)
  return content.to_s unless content.is_a?(Array)

  user_display_text(message)
end