Module: Layered::Assistant::MessagesHelper

Defined in:
app/helpers/layered/assistant/messages_helper.rb

Constant Summary collapse

ALLOWED_TAGS =
%w[
  p br
  h1 h2 h3 h4 h5 h6
  strong em s del
  ul ol li
  pre code
  a
  blockquote
  table thead tbody tr th td
  hr
].freeze
ALLOWED_ATTRIBUTES =
%w[href title class].freeze
MIN_RESPONSE_MS_FOR_TPS =
100

Instance Method Summary collapse

Instance Method Details

#message_metadata_title(message) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'app/helpers/layered/assistant/messages_helper.rb', line 20

def (message)
  total_tokens = message.input_tokens.to_i + message.output_tokens.to_i
  parts = []
  if total_tokens > 0
    prefix = message.tokens_estimated? ? "~" : ""
    parts << "#{prefix}#{number_with_delimiter(total_tokens)} tokens"
  end
  if message.output_tokens.to_i > 0 && message.response_ms.to_i >= MIN_RESPONSE_MS_FOR_TPS
    tps = (message.output_tokens * 1000.0 / message.response_ms).round(1)
    parts << "#{tps} tok/s"
  end
  parts << "#{message.ttft_ms}ms TTFT" if message.ttft_ms
  parts.join(" · ")
end

#render_message_content(message) ⇒ Object



35
36
37
38
39
# File 'app/helpers/layered/assistant/messages_helper.rb', line 35

def render_message_content(message)
  return if message.content.blank?

  render_markdown(message.content)
end

#render_streaming_markdown(content) ⇒ Object

Renders accumulated content for streaming, holding back any trailing unclosed code fence that Kramdown can’t parse correctly. Returns { html:, has_unclosed_fence: } so the caller can decide whether to show a typing indicator.



45
46
47
48
49
50
51
52
53
# File 'app/helpers/layered/assistant/messages_helper.rb', line 45

def render_streaming_markdown(content)
  return { html: "", has_unclosed_fence: false } if content.blank?

  safe = strip_unclosed_fence(content)
  {
    html: safe.present? ? render_markdown(safe) : "",
    has_unclosed_fence: safe.length < content.length
  }
end