# 🔬 VERIFICATION: LangGraph Streaming Behavior

## 🎯 MỤC ĐÍCH

Kiểm tra xem LangGraph `astream()` có stream **incremental** (từng phần) hay chỉ emit event **sau khi node hoàn thành**.

---

## 📊 KẾT QUẢ EXPECTED

### **Scenario 1: Incremental Streaming (Lý tưởng)** ✅

Nếu LangGraph stream incremental, backend logs sẽ hiển thị:

```
🌊 Starting LLM streaming...
📦 Event #1 at t=2.50s | Keys: ['messages']
📦 Event #2 at t=3.20s | Keys: ['ai_response']
📡 Event #2 (t=3.20s): ai_response with 150 chars
   Preview: {"ai_response": "Anh chọn áo thun th...
📦 Event #3 at t=4.10s | Keys: ['ai_response']
📡 Event #3 (t=4.10s): ai_response with 380 chars
   Preview: {"ai_response": "Anh chọn áo thun thể thao nam chuẩn luôn! Em tìm...
📦 Event #4 at t=5.50s | Keys: ['ai_response']
📡 Event #4 (t=5.50s): ai_response with 620 chars
   Preview: {"ai_response": "...", "product_ids": ["SKU1", "SKU2"]...
🎯 Event #4 (t=5.50s): Regex matched product_ids!
✅ Extracted 3 SKUs: ['SKU1', 'SKU2', 'SKU3']
🚨 BREAKING at Event #4 (t=5.50s) - user_insight KHÔNG ĐỢI!
```

**→ Content tăng dần (150 → 380 → 620 chars)**  
**→ Break sớm khi có product_ids (t=5.5s thay vì t=12s)**

---

### **Scenario 2: Event-based (Sau khi xong)** ❌

Nếu LangGraph chỉ emit sau khi node xong, logs sẽ là:

```
🌊 Starting LLM streaming...
📦 Event #1 at t=2.30s | Keys: ['messages']  ← Tool execution
📦 Event #2 at t=11.80s | Keys: ['ai_response']  ← LLM node hoàn thành
📡 Event #2 (t=11.80s): ai_response with 1250 chars  ← TOÀN BỘ RESPONSE
   Preview: {"ai_response": "Anh chọn áo thun thể thao nam chuẩn luôn!...", "product_ids": ["SKU1", "SKU2", "SKU3"], "user_insight": {...}}
🎯 Event #2 (t=11.80s): Regex matched product_ids!
✅ Extracted 3 SKUs: ['SKU1', 'SKU2', 'SKU3']
🚨 BREAKING at Event #2 (t=11.80s) - user_insight KHÔNG ĐỢI!
```

**→ CHỈ 1 EVENT duy nhất với full content**  
**→ Emit sau khi LLM xong hết (t=11.8s)**  
**→ KHÔNG THỂ break sớm hơn!**

---

## 🔍 PHÂN TÍCH

### **Nếu Scenario 2 (Event-based):**

**Giải thích:**
- LLM **đang stream tokens internal** từ t=2s → t=12s
- LangGraph **chờ node xong** mới emit event
- Event chứa **full response** luôn
- Regex match ngay lập tức vì đã có đầy đủ

**Kết luận:**
- ✅ Code đã đúng, streaming đã bật
- ❌ Nhưng không thể break sớm hơn vì event chưa có
- ⏱️ Latency không giảm được (~12s)

---

## 💡 GIẢI PHÁP

Nếu kết quả là Scenario 2, muốn stream thực sự cần:

### **Option A: Custom Streaming Callback**
```python
from langchain.callbacks.base import AsyncCallbackHandler

class StreamingCallback(AsyncCallbackHandler):
    async def on_llm_new_token(self, token: str, **kwargs):
        # Accumulate và check regex
        self.accumulated += token
        if '"product_ids"' in self.accumulated:
            # Trigger break somehow
            pass
```

### **Option B: SSE Endpoint**
Stream events trực tiếp cho client, client tự parse

### **Option C: Giữ nguyên**
Code đã tối ưu trong giới hạn, accept latency

---

## 📝 NOTES

- **Streaming=True** trong LLM → LangChain stream tokens internal
- **graph.astream()** → Stream events, không phải tokens
- **Break early** chỉ có ý nghĩa nếu events emit incremental

**Hãy check logs backend để xác định scenario nào!**
