feat(wallet): nối trợ lý với ví AI qua check-limit và deduct
Gateway là nơi duy nhất nối được với ví vì chỉ nó có đồng thời contact.token (từ Salework verify) và companyCode. ai-core có token nhưng không biết companyCode; memory-service không có cả hai.
Không đổi hợp đồng API nào: TOKEN_USAGE_ENDPOINT của ai-core và memory-service trỏ về POST /internal/usage, nên cơ chế gom token đã xây từ trước trở thành đường dữ liệu vào ví. Luồng: check-limit chặn trước khi gọi ai-core, lưu ngữ cảnh theo trace_id (RAM, TTL 900s), rồi mỗi báo cáo token thành một lần deduct nối lại đúng công ty.
Ba quyết định đáng ghi lại:
- Ví trả lỗi nghiệp vụ bằng HTTP 200 kèm data.error, nên chỉ nhìn status code là tưởng đã trừ được tiền trong khi không trừ gì.
- cached_tokens là tập con của input, phải trừ ra trước khi nhân đơn giá đầy đủ, nếu không là tính dư tiền của khách hàng.
- NO_WALLET cũng trả exceeded=true; chặn cứng đồng nghĩa tắt trợ lý của mọi công ty chưa lập ví, nên mặc định cho chạy.
Bảng giá để ở pricing/llm_prices.json và nạp lại theo mtime — đổi giá là việc của vận hành, không phải của một lần release. Model chưa khai giá được tính 0 USD và đếm vào wallet_unpriced_events_total thay vì đoán bừa một đơn giá.
WALLET_ENABLED=false nên bản này chưa chặn và chưa trừ đồng nào; còn chờ đội ví xác nhận số lần trừ trên một transactionId, tính duy nhất của transactionCode, số chữ số làm tròn costUsd và TTL của check-limit.
133 test pass.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com