Kward

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



43
44
45
46
47
48
49
50
51
52
53
# File 'lib/kward/cli_transcript_formatter.rb', line 43

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



107
108
109
110
111
112
113
114
115
116
# File 'lib/kward/cli_transcript_formatter.rb', line 107

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



34
35
36
37
38
39
40
41
# File 'lib/kward/cli_transcript_formatter.rb', line 34

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



144
145
146
147
148
149
150
# File 'lib/kward/cli_transcript_formatter.rb', line 144

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

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

.display_text(message) ⇒ Object



55
56
57
58
59
60
# File 'lib/kward/cli_transcript_formatter.rb', line 55

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

  content_text(MessageAccess.content(message))
end

.full_text(message) ⇒ Object



103
104
105
# File 'lib/kward/cli_transcript_formatter.rb', line 103

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

.image_part_reference(part) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/kward/cli_transcript_formatter.rb', line 130

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



84
85
86
87
88
89
# File 'lib/kward/cli_transcript_formatter.rb', line 84

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



91
92
93
# File 'lib/kward/cli_transcript_formatter.rb', line 91

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

.reasoning(message) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/kward/cli_transcript_formatter.rb', line 12

def reasoning(message)
  direct = MessageAccess.value(message, :reasoning_summary)
  return direct.to_s unless direct.to_s.empty?

  content = MessageAccess.content(message)
  if content.is_a?(Array)
    text = 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.join("\n")
    return text unless text.empty?
  end

  response_items(message).filter_map do |item|
    next unless MessageAccess.value(item, :type) == "reasoning"

    response_item_text(MessageAccess.value(item, :summary)).empty? ? response_item_text(MessageAccess.value(item, :content)) : response_item_text(MessageAccess.value(item, :summary))
  end.join("\n")
end

.response_item_text(parts) ⇒ Object



122
123
124
125
126
127
128
# File 'lib/kward/cli_transcript_formatter.rb', line 122

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

    MessageAccess.value(part, :text) || MessageAccess.value(part, :refusal)
  end.join
end

.response_items(message) ⇒ Object



118
119
120
# File 'lib/kward/cli_transcript_formatter.rb', line 118

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

.synthetic_tool_call(name, id) ⇒ Object



95
96
97
98
99
100
101
# File 'lib/kward/cli_transcript_formatter.rb', line 95

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

.user_display_text(message) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/kward/cli_transcript_formatter.rb', line 62

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



77
78
79
80
81
82
# File 'lib/kward/cli_transcript_formatter.rb', line 77

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

  user_display_text(message)
end