Chuyển tới nội dung chính

Brand Assistant — Hiển thị Trợ lý Nebula

Khi session có brand config, hiển thị header trợ lý (icon + tên) trước AI response.


Quy tắc hiển thị

Brand header hiển thị bằng cách kết hợp 2 cơ chế:

1. History (F5 reload)

Block từ assistant message có message_type: "chat" → brand header trước block đó.

2. Stream (realtime — send chat mới)

Sau mỗi user message → brand header hiển thị ngay lập tức (trước loading dots), không chờ AI response.

Quy tắc chung

  • brand: null → không hiển thị
  • User message → KHÔNG có brand
  • message_type: "step" → KHÔNG có brand
  • Brand nằm ngoài group
👤 thị trường hôm nay                ← user message

⚛ Nebula ← brand (ngay lập tức, trước loading)
●●● ← loading dots (đang chờ AI)

⚛ Nebula ← brand (từ history message_type: "chat")
Chào Thảo! Để mình cập nhật... ← share_thought text

Phân tích giá VNINDEX ˅ ← group (step, no brand)
● analyze_price
✓ Hoàn thành

⚛ Nebula ← brand (từ history message_type: "chat")
VNINDEX hôm nay tăng 2.69%... ← final answer

History API — brand field

{
"session_id": "abc-123",
"messages": [...],
"brand": {
"icon": "nebula",
"name": "Nebula",
"color": "#4D4D4D"
}
}
FieldTypeMô tả
iconstring"nebula" hoặc "simplize"
namestringTên hiển thị
colorstringMàu icon (hex)

message_type trên blocks (History)

Backend gán message_type vào block đầu tiên của mỗi assistant chat message:

Assistant message_type: "chat", display_type: "content"
→ block đầu tiên (thinking/text): message_type = "chat" ✓

Assistant message_type: "chat", display_type: "group_start"
→ group_start marker: message_type = "chat" ✓

Assistant message_type: "step"
→ không gán message_type

Render Logic

let needBrand = false;

for (const item of groupedItems) {
// User message → set flag, brand hiện ở item tiếp theo
if (item.type === 'user') {
needBrand = !!brand;
render(<UserMessage />);
continue;
}

// Show brand: stream (needBrand) HOẶC history (message_type === 'chat')
const showBrand = needBrand || (brand && item.message_type === 'chat');
if (needBrand) needBrand = false;

if (showBrand) render(<BrandHeader />);

if (item.type === '_group') {
render(<GroupBlock />); // brand nằm ngoài, trước group
} else {
render(<Block />);
}
}

// Cuối: nếu needBrand còn true (chưa có AI block) → show brand trước loading
if (needBrand) render(<BrandHeader />);
if (isStreaming) render(<LoadingDots />);

Brand Header Component

function BrandHeader({ brand }) {
return (
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4, marginTop: 20 }}>
<BrandIcon icon={brand.icon} size={22} color={brand.color} />
<span style={{ fontSize: 15, fontWeight: 600, color: '#374151' }}>{brand.name}</span>
</div>
);
}

Brand Icons

Icon và style theo design system. Render dựa trên brand.icon field.


Checklist

  • Parse brand từ history response
  • BrandHeader component (icon + name + color)
  • History: brand trước block có message_type === "chat"
  • Stream: brand ngay sau user message (trước loading dots)
  • Brand nằm ngoài group
  • Không hiển thị cho user messages / step messages
  • Không hiển thị khi brand: null
  • Support icon types: nebula, simplize