File size: 6,777 Bytes
8981bf6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
{# HTML fragment returned by POST /chat and appended to the chat thread by htmx. #}

{# ----- User message (right) ----- #}
<div class="sc-fade flex items-start justify-end gap-2.5">
  <div class="sc-bubble sc-bubble--user">{{ message }}</div>
  <span class="mt-0.5 grid h-8 w-8 shrink-0 place-items-center rounded-full bg-sand-200 text-sand-500 dark:bg-sand-700 dark:text-sand-300">
    <svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
  </span>
</div>

{# ----- Agent reply (left) ----- #}
<div class="sc-fade flex items-start gap-2.5">
  <span class="sc-grad mt-0.5 grid h-8 w-8 shrink-0 place-items-center rounded-full text-white">
    <svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>
  </span>

  <div class="min-w-0 flex-1 space-y-2.5">
    {# Status / intent / confidence badges #}
    <div class="flex flex-wrap items-center gap-1.5">
      {% if resp.escalated %}
      <span class="sc-badge sc-badge--escalate">
        <svg class="h-3 w-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 11h-6"/></svg>
        Escalated to human
      </span>
      {% elif resp.auto_resolved %}
      <span class="sc-badge sc-badge--resolved">
        <svg class="h-3 w-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
        Auto-resolved
      </span>
      {% endif %}
      <span class="sc-badge sc-badge--neutral">{{ resp.intent | replace('_', ' ') }}</span>
    </div>

    {# Answer bubble #}
    <div class="sc-bubble sc-bubble--agent text-sand-700 dark:text-sand-200">
      {{ resp.answer }}
    </div>

    {# Confidence meter #}
    <div class="flex items-center gap-2 pl-1">
      <span class="text-[11px] font-semibold text-sand-400 dark:text-sand-500">Confidence</span>
      <div class="sc-meter w-24"><span style="width: {{ (resp.confidence * 100) | round(0, 'floor') }}%"></span></div>
      <span class="text-[11px] font-bold text-sand-500 dark:text-sand-400">{{ '%.0f' % (resp.confidence * 100) }}%</span>
    </div>

    {# Refund / escalation decision card #}
    {% if resp.refund %}
    <div class="sc-decision sc-decision--{{ resp.refund.outcome }}">
      <div class="flex items-center gap-2">
        {% if resp.refund.outcome == 'approve' %}
        <svg class="h-4 w-4 text-emerald-600 dark:text-emerald-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>
        {% elif resp.refund.outcome == 'deny' %}
        <svg class="h-4 w-4 text-red-500" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>
        {% else %}
        <svg class="h-4 w-4 text-amber-600 dark:text-amber-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
        {% endif %}
        <span class="text-sm font-bold text-sand-800 dark:text-sand-100">
          Refund decision: {{ resp.refund.outcome | upper }}
          {% if resp.refund.outcome == 'approve' %}
            · ${{ '%.2f' % resp.refund.refund_amount }}
            {% if resp.refund.fee_applied %}<span class="font-normal text-sand-500 dark:text-sand-400">(after ${{ '%.2f' % resp.refund.fee_applied }} return-shipping fee)</span>{% endif %}
          {% endif %}
        </span>
      </div>
      <p class="mt-1.5 text-sm text-sand-600 dark:text-sand-300">{{ resp.refund.reason }}</p>
      <div class="mt-2 flex items-center gap-1.5 text-xs text-sand-500 dark:text-sand-400">
        <svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
        Per <span class="font-semibold text-sand-600 dark:text-sand-300">{{ resp.refund.policy_citation }}</span>
      </div>
    </div>
    {% endif %}

    {# Escalation explainer card (no refund object but escalated) #}
    {% if resp.escalated and not resp.refund %}
    <div class="sc-decision sc-decision--escalate flex items-start gap-2">
      <svg class="mt-0.5 h-4 w-4 shrink-0 text-amber-600 dark:text-amber-400" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.81.36 1.6.7 2.34a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.74-1.27a2 2 0 0 1 2.11-.45c.74.34 1.53.57 2.34.7A2 2 0 0 1 22 16.92z"/></svg>
      <p class="text-sm text-sand-600 dark:text-sand-300">
        Handed off to a human specialist — the agent escalates sensitive or low-confidence
        cases instead of guessing.
      </p>
    </div>
    {% endif %}

    {# Citations as chips #}
    {% if resp.citations %}
    <div>
      <p class="mb-1.5 flex items-center gap-1.5 text-xs font-semibold text-sand-400 dark:text-sand-500">
        <svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
        Sources ({{ resp.citations | length }})
      </p>
      <div class="flex flex-wrap gap-1.5">
        {% for c in resp.citations %}
        <span class="sc-chip sc-chip--{{ c.kind }}" title="{{ c.snippet }}">
          <span class="sc-chip-dot"></span>{{ c.title }}
        </span>
        {% endfor %}
      </div>
    </div>
    {% endif %}
  </div>
</div>