Module: SqlChatbot::Grammar::ListRenderer

Defined in:
lib/sql_chatbot/grammar/list_renderer.rb

Overview

Programmatic renderer for the grammar’s LIST primitive when the result is small. Bypasses the answer-stream LLM so it can’t drop or truncate.

Constant Summary collapse

THRESHOLD =
10
PREFERRED_LABEL_KEYS =
%w[title name label subject email username].freeze

Class Method Summary collapse

Class Method Details

.pick_label(row) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
# File 'lib/sql_chatbot/grammar/list_renderer.rb', line 37

def self.pick_label(row)
  PREFERRED_LABEL_KEYS.each do |k|
    v = row[k] || row[k.to_sym]
    return v.strip if v.is_a?(String) && !v.strip.empty?
  end
  row.each do |k, v|
    next if k.to_s == "id"
    return v.strip if v.is_a?(String) && !v.strip.empty?
  end
  nil
end

.try_render(primitive, entity_display_label, rows) ⇒ Object

Returns { ok: true, text: “…” } when conditions met, else { ok: false }.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/sql_chatbot/grammar/list_renderer.rb', line 15

def self.try_render(primitive, entity_display_label, rows)
  return { ok: false } unless primitive.to_s == "LIST"
  return { ok: false } unless rows.is_a?(Array)
  return { ok: true, text: ProgrammaticRenderer.empty_text(entity_display_label) } if rows.empty?
  return { ok: false } if rows.length > THRESHOLD

  labels = []
  rows.each do |row|
    return { ok: false } unless row.is_a?(Hash)
    lbl = pick_label(row)
    return { ok: false } unless lbl
    labels << lbl
  end

  label_or_item = entity_display_label.to_s.empty? ? "item" : entity_display_label.to_s
  noun = rows.length == 1 ? CountRenderer.to_singular_label(label_or_item) : CountRenderer.to_plural_label(label_or_item)

  intro = rows.length == 1 ? "Here is the #{noun}:" : "Here are the #{rows.length} #{noun}:"
  lines = labels.map { |l| "- #{l}" }.join("\n")
  { ok: true, text: "#{intro}\n#{lines}" }
end