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"
}
}
| Field | Type | Mô tả |
|---|---|---|
icon | string | "nebula" hoặc "simplize" |
name | string | Tên hiển thị |
color | string | Mà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
brandtừ history response -
BrandHeadercomponent (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