Commit 90c34457 authored by Vũ Hoàng Anh's avatar Vũ Hoàng Anh

update

parent 662e55c8
- generic [active] [ref=e1]:
- complementary [ref=e2]:
- generic [ref=e3]:
- generic [ref=e4]: CA
- generic [ref=e5]:
- heading "Canifa AI" [level=2] [ref=e6]
- text: Admin Console
- generic [ref=e7]:
- generic [ref=e8]:
- generic [ref=e9]: Main
- generic [ref=e10] [cursor=pointer]:
- generic [ref=e11]: RM
- generic [ref=e12]: Kế hoạch phát triển
- generic [ref=e15] [cursor=pointer]: Sơ đồ hoạt động
- generic [ref=e16] [cursor=pointer]:
- generic [ref=e18]: Chatbot
- generic [ref=e19]: LIVE
- generic [ref=e22] [cursor=pointer]: History
- generic [ref=e25] [cursor=pointer]: Product Perf.
- generic [ref=e26] [cursor=pointer]:
- generic [ref=e28]: AI Data Analyst
- generic [ref=e29]: NEW
- generic [ref=e30] [cursor=pointer]:
- generic [ref=e31]: SQL
- generic [ref=e32]: AI sinh SQL
- generic [ref=e33]: NEW
- generic [ref=e34] [cursor=pointer]:
- generic [ref=e35]: LIVE
- generic [ref=e36]: Realtime Monitor
- generic [ref=e37]: LIVE
- generic [ref=e38] [cursor=pointer]:
- generic [ref=e39]: PO
- generic [ref=e40]: Prompt Optimizer
- generic [ref=e41]: NEW
- generic [ref=e42] [cursor=pointer]:
- generic [ref=e43]: US
- generic [ref=e44]: User Simulator
- generic [ref=e45]: NEW
- generic [ref=e46] [cursor=pointer]:
- generic [ref=e47]: UI
- generic [ref=e48]: User Insight
- generic [ref=e49]: NEW
- generic [ref=e50] [cursor=pointer]:
- generic [ref=e51]: RT
- generic [ref=e52]: Regression Test
- generic [ref=e53]: NEW
- generic [ref=e54] [cursor=pointer]:
- generic [ref=e55]: ST
- generic [ref=e56]: Stress Test
- generic [ref=e57]: NEW
- generic [ref=e58] [cursor=pointer]:
- generic [ref=e59]: CR
- generic [ref=e60]: Competitor Research
- generic [ref=e61]: NEW
- generic [ref=e62]:
- generic [ref=e63]: Workspace
- generic [ref=e66] [cursor=pointer]: Resources
- generic [ref=e69] [cursor=pointer]: Team Notes
- generic [ref=e72] [cursor=pointer]: Changelog
- generic [ref=e75] [cursor=pointer]: Hướng dẫn
- generic [ref=e76]:
- generic [ref=e77]: Thử nghiệm
- generic [ref=e78] [cursor=pointer]:
- generic [ref=e80]: Text-to-SQL
- generic [ref=e81]: BETA
- generic [ref=e82] [cursor=pointer]:
- generic [ref=e84]: DB Test
- generic [ref=e85]: BETA
- generic [ref=e86] [cursor=pointer]:
- generic [ref=e88]: Feedback Demo
- generic [ref=e89]: NEW
- generic [ref=e90] [cursor=pointer]:
- generic [ref=e92]: Chatbot (Dev)
- generic [ref=e93]: DEV
- generic [ref=e96] [cursor=pointer]: Cache Manager
- generic [ref=e97]:
- generic [ref=e98]: External
- link "API Docs" [ref=e99] [cursor=pointer]:
- /url: /docs
- generic [ref=e101]: API Docs
- link "ReDoc" [ref=e102] [cursor=pointer]:
- /url: /redoc
- generic [ref=e104]: ReDoc
- generic [ref=e106]:
- generic [ref=e107]:
- generic [ref=e108]: U
- generic [ref=e109]: user
- button "⚙" [ref=e110]
- button "⏻" [ref=e111]
- generic [ref=e112]:
- strong [ref=e114]: v2.5.0
- text: · Online
- iframe [ref=e116]:
- generic [ref=f1e2]:
- generic [ref=f1e4]:
- heading "📝 Team Notes" [level=1] [ref=f1e5]
- paragraph [ref=f1e6]: Notes, pinned docs & team workspace
- generic [ref=f1e7]:
- generic [ref=f1e8]:
- heading "📝 Team Notes & Pinned Docs" [level=2] [ref=f1e9]
- button "+ New Note" [ref=f1e10] [cursor=pointer]
- generic [ref=f1e11]:
- button "All" [ref=f1e12] [cursor=pointer]
- button "📌 Pinned" [ref=f1e13] [cursor=pointer]
- button "📝 Notes" [ref=f1e14] [cursor=pointer]
- button "📄 Docs" [ref=f1e15] [cursor=pointer]
- button "? To-do" [ref=f1e16] [cursor=pointer]
- button "📢 Announcements" [ref=f1e17] [cursor=pointer]
- generic [ref=f1e18]:
- generic [ref=f1e19]:
- generic [ref=f1e20]:
- generic [ref=f1e21]: 📝 note
- generic [ref=f1e22]: 📌
- generic [ref=f1e23]: v2.0 — Nâng cấp lớn (kế hoạch)
- generic [ref=f1e24]: "Prompt Engineering v2: rewrite toàn bộ, multi-turn context, persona nâng cao Giao diện chatbot mới: dark mode, product cards, quick replies Multi-Agent: Sales Agent + Support Agent + Analytics Agent phối hợp A/B Testing Engine: so sánh prompt/model variants trên live traffic Customer Analytics Dashboard: phân tích hành vi khách hàng từ chat data"
- generic [ref=f1e25]:
- generic [ref=f1e26]: 27 ph�t tru?c
- generic [ref=f1e27]:
- button "✏️" [ref=f1e28] [cursor=pointer]
- button "✏️" [ref=f1e29] [cursor=pointer]
- button "🗑️" [ref=f1e30] [cursor=pointer]
- generic [ref=f1e31]:
- generic [ref=f1e32]:
- generic [ref=f1e33]: 📝 note
- generic [ref=f1e34]: 📌
- generic [ref=f1e35]: v1.1 — Đổi model (thử nghiệm)
- generic [ref=f1e36]: "Chuyển GPT-4.1-mini Gemini 3.1 Flash-Lite Giảm ~55% chi phí per request Rút gọn prompt 56%, format phù hợp Gemini Benchmark: 20 test cases so sánh Score/Latency/Cost chế rollback về GPT nếu Gemini không ổn"
- generic [ref=f1e37]:
- generic [ref=f1e38]: 3 ng�y tru?c
- generic [ref=f1e39]:
- button "✏️" [ref=f1e40] [cursor=pointer]
- button "✏️" [ref=f1e41] [cursor=pointer]
- button "🗑️" [ref=f1e42] [cursor=pointer]
- generic [ref=f1e43]:
- generic [ref=f1e44]:
- generic [ref=f1e45]: 📝 note
- generic [ref=f1e46]: 📌
- generic [ref=f1e47]: v1.0 — Production
- generic [ref=f1e48]: "Model: GPT-4.1-mini Search: Vector search (pgvector, IVFFlat) Stock: API Magento realtime Prompt: Module v1 (persona, guardrails, sales flow) Observability: Langfuse tracing + scoring Chat: Lưu lịch sử + Like/Dislike feedback"
- generic [ref=f1e49]:
- generic [ref=f1e50]: 3 ng�y tru?c
- generic [ref=f1e51]:
- button "✏️" [ref=f1e52] [cursor=pointer]
- button "✏️" [ref=f1e53] [cursor=pointer]
- button "🗑️" [ref=f1e54] [cursor=pointer]
- generic [ref=f1e55]:
- generic [ref=f1e57]: 📝 note
- generic [ref=f1e58]: v1.2 — Đổi logic lấy dữ liệu (thử nghiệm)
- generic [ref=f1e59]: "Hybrid Search: vector + keyword thay vector-only HNSW Index thay IVFFlat recall tăng ~15% Filters nâng cao: lọc giá, màu, size, danh mục trước khi search Caching layer (Redis): cache kết quả search phổ biến, giảm latency"
- generic [ref=f1e60]:
- generic [ref=f1e61]: 27 ph�t tru?c
- generic [ref=f1e62]:
- button "✏️" [ref=f1e63] [cursor=pointer]
- button "✏️" [ref=f1e64] [cursor=pointer]
- button "🗑️" [ref=f1e65] [cursor=pointer]
\ No newline at end of file
- generic [active] [ref=e1]:
- complementary [ref=e2]:
- generic [ref=e3]:
- generic [ref=e4]: CA
- generic [ref=e5]:
- heading "Canifa AI" [level=2] [ref=e6]
- text: Admin Console
- generic [ref=e7]:
- generic [ref=e8]:
- generic [ref=e9]: Main
- generic [ref=e10] [cursor=pointer]:
- generic [ref=e11]: RM
- generic [ref=e12]: Kế hoạch phát triển
- generic [ref=e15] [cursor=pointer]: Sơ đồ hoạt động
- generic [ref=e16] [cursor=pointer]:
- generic [ref=e18]: Chatbot
- generic [ref=e19]: LIVE
- generic [ref=e22] [cursor=pointer]: History
- generic [ref=e25] [cursor=pointer]: Product Perf.
- generic [ref=e26] [cursor=pointer]:
- generic [ref=e28]: AI Data Analyst
- generic [ref=e29]: NEW
- generic [ref=e30] [cursor=pointer]:
- generic [ref=e31]: SQL
- generic [ref=e32]: AI sinh SQL
- generic [ref=e33]: NEW
- generic [ref=e34] [cursor=pointer]:
- generic [ref=e35]: LIVE
- generic [ref=e36]: Realtime Monitor
- generic [ref=e37]: LIVE
- generic [ref=e38] [cursor=pointer]:
- generic [ref=e39]: PO
- generic [ref=e40]: Prompt Optimizer
- generic [ref=e41]: NEW
- generic [ref=e42] [cursor=pointer]:
- generic [ref=e43]: US
- generic [ref=e44]: User Simulator
- generic [ref=e45]: NEW
- generic [ref=e46] [cursor=pointer]:
- generic [ref=e47]: UI
- generic [ref=e48]: User Insight
- generic [ref=e49]: NEW
- generic [ref=e50] [cursor=pointer]:
- generic [ref=e51]: RT
- generic [ref=e52]: Regression Test
- generic [ref=e53]: NEW
- generic [ref=e54] [cursor=pointer]:
- generic [ref=e55]: ST
- generic [ref=e56]: Stress Test
- generic [ref=e57]: NEW
- generic [ref=e58] [cursor=pointer]:
- generic [ref=e59]: CR
- generic [ref=e60]: Competitor Research
- generic [ref=e61]: NEW
- generic [ref=e62]:
- generic [ref=e63]: Workspace
- generic [ref=e66] [cursor=pointer]: Resources
- generic [ref=e69] [cursor=pointer]: Team Notes
- generic [ref=e72] [cursor=pointer]: Changelog
- generic [ref=e75] [cursor=pointer]: Hướng dẫn
- generic [ref=e76]:
- generic [ref=e77]: Thử nghiệm
- generic [ref=e78] [cursor=pointer]:
- generic [ref=e80]: Text-to-SQL
- generic [ref=e81]: BETA
- generic [ref=e82] [cursor=pointer]:
- generic [ref=e84]: DB Test
- generic [ref=e85]: BETA
- generic [ref=e86] [cursor=pointer]:
- generic [ref=e88]: Feedback Demo
- generic [ref=e89]: NEW
- generic [ref=e90] [cursor=pointer]:
- generic [ref=e92]: Chatbot (Dev)
- generic [ref=e93]: DEV
- generic [ref=e96] [cursor=pointer]: Cache Manager
- generic [ref=e97]:
- generic [ref=e98]: External
- link "API Docs" [ref=e99] [cursor=pointer]:
- /url: /docs
- generic [ref=e101]: API Docs
- link "ReDoc" [ref=e102] [cursor=pointer]:
- /url: /redoc
- generic [ref=e104]: ReDoc
- generic [ref=e106]:
- generic [ref=e107]:
- generic [ref=e108]: U
- generic [ref=e109]: user
- button "⚙" [ref=e110]
- button "⏻" [ref=e111]
- generic [ref=e112]:
- strong [ref=e114]: v2.5.0
- text: · Online
- iframe [ref=e116]:
- generic [ref=f2e2]:
- generic [ref=f2e4]:
- heading "📝 Team Notes" [level=1] [ref=f2e5]
- paragraph [ref=f2e6]: Notes, pinned docs & team workspace
- generic [ref=f2e7]:
- generic [ref=f2e8]:
- heading "📝 Team Notes & Pinned Docs" [level=2] [ref=f2e9]
- button "+ New Note" [ref=f2e10] [cursor=pointer]
- generic [ref=f2e11]:
- button "All" [ref=f2e12] [cursor=pointer]
- button "📌 Pinned" [ref=f2e13] [cursor=pointer]
- button "📝 Notes" [ref=f2e14] [cursor=pointer]
- button "📄 Docs" [ref=f2e15] [cursor=pointer]
- button "✅ To-do" [ref=f2e16] [cursor=pointer]
- button "📢 Announcements" [ref=f2e17] [cursor=pointer]
- generic [ref=f2e18]:
- generic [ref=f2e19]:
- generic [ref=f2e20]:
- generic [ref=f2e21]: 📝 note
- generic [ref=f2e22]: 📌
- generic [ref=f2e23]: v2.0 — Nâng cấp lớn (kế hoạch)
- generic [ref=f2e24]: "Prompt Engineering v2: rewrite toàn bộ, multi-turn context, persona nâng cao Giao diện chatbot mới: dark mode, product cards, quick replies Multi-Agent: Sales Agent + Support Agent + Analytics Agent phối hợp A/B Testing Engine: so sánh prompt/model variants trên live traffic Customer Analytics Dashboard: phân tích hành vi khách hàng từ chat data"
- generic [ref=f2e25]:
- generic [ref=f2e26]: 38 phút trước
- generic [ref=f2e27]:
- button "✏️" [ref=f2e28] [cursor=pointer]
- button "✏️" [ref=f2e29] [cursor=pointer]
- button "🗑️" [ref=f2e30] [cursor=pointer]
- generic [ref=f2e31]:
- generic [ref=f2e32]:
- generic [ref=f2e33]: 📝 note
- generic [ref=f2e34]: 📌
- generic [ref=f2e35]: v1.1 — Đổi model (thử nghiệm)
- generic [ref=f2e36]: "Chuyển GPT-4.1-mini Gemini 3.1 Flash-Lite Giảm ~55% chi phí per request Rút gọn prompt 56%, format phù hợp Gemini Benchmark: 20 test cases so sánh Score/Latency/Cost chế rollback về GPT nếu Gemini không ổn"
- generic [ref=f2e37]:
- generic [ref=f2e38]: 3 ngày trước
- generic [ref=f2e39]:
- button "✏️" [ref=f2e40] [cursor=pointer]
- button "✏️" [ref=f2e41] [cursor=pointer]
- button "🗑️" [ref=f2e42] [cursor=pointer]
- generic [ref=f2e43]:
- generic [ref=f2e44]:
- generic [ref=f2e45]: 📝 note
- generic [ref=f2e46]: 📌
- generic [ref=f2e47]: v1.0 — Production
- generic [ref=f2e48]: "Model: GPT-4.1-mini Search: Vector search (pgvector, IVFFlat) Stock: API Magento realtime Prompt: Module v1 (persona, guardrails, sales flow) Observability: Langfuse tracing + scoring Chat: Lưu lịch sử + Like/Dislike feedback"
- generic [ref=f2e49]:
- generic [ref=f2e50]: 3 ngày trước
- generic [ref=f2e51]:
- button "✏️" [ref=f2e52] [cursor=pointer]
- button "✏️" [ref=f2e53] [cursor=pointer]
- button "🗑️" [ref=f2e54] [cursor=pointer]
- generic [ref=f2e55]:
- generic [ref=f2e57]: 📝 note
- generic [ref=f2e58]: v1.2 — Đổi logic lấy dữ liệu (thử nghiệm)
- generic [ref=f2e59]: "Hybrid Search: vector + keyword thay vector-only HNSW Index thay IVFFlat recall tăng ~15% Filters nâng cao: lọc giá, màu, size, danh mục trước khi search Caching layer (Redis): cache kết quả search phổ biến, giảm latency"
- generic [ref=f2e60]:
- generic [ref=f2e61]: 38 phút trước
- generic [ref=f2e62]:
- button "✏️" [ref=f2e63] [cursor=pointer]
- button "✏️" [ref=f2e64] [cursor=pointer]
- button "🗑️" [ref=f2e65] [cursor=pointer]
\ No newline at end of file
- generic [active] [ref=e1]:
- complementary [ref=e2]:
- generic [ref=e3]:
- generic [ref=e4]: CA
- generic [ref=e5]:
- heading "Canifa AI" [level=2] [ref=e6]
- text: Admin Console
- generic [ref=e7]:
- generic [ref=e8]:
- generic [ref=e9]: Main
- generic [ref=e10] [cursor=pointer]:
- generic [ref=e11]: RM
- generic [ref=e12]: Kế hoạch phát triển
- generic [ref=e15] [cursor=pointer]: Sơ đồ hoạt động
- generic [ref=e16] [cursor=pointer]:
- generic [ref=e18]: Chatbot
- generic [ref=e19]: LIVE
- generic [ref=e22] [cursor=pointer]: History
- generic [ref=e25] [cursor=pointer]: Product Perf.
- generic [ref=e26] [cursor=pointer]:
- generic [ref=e28]: AI Data Analyst
- generic [ref=e29]: NEW
- generic [ref=e30] [cursor=pointer]:
- generic [ref=e31]: SQL
- generic [ref=e32]: AI sinh SQL
- generic [ref=e33]: NEW
- generic [ref=e34] [cursor=pointer]:
- generic [ref=e35]: LIVE
- generic [ref=e36]: Realtime Monitor
- generic [ref=e37]: LIVE
- generic [ref=e38] [cursor=pointer]:
- generic [ref=e39]: PO
- generic [ref=e40]: Prompt Optimizer
- generic [ref=e41]: NEW
- generic [ref=e42] [cursor=pointer]:
- generic [ref=e43]: US
- generic [ref=e44]: User Simulator
- generic [ref=e45]: NEW
- generic [ref=e46] [cursor=pointer]:
- generic [ref=e47]: UI
- generic [ref=e48]: User Insight
- generic [ref=e49]: NEW
- generic [ref=e50] [cursor=pointer]:
- generic [ref=e51]: RT
- generic [ref=e52]: Regression Test
- generic [ref=e53]: NEW
- generic [ref=e54] [cursor=pointer]:
- generic [ref=e55]: ST
- generic [ref=e56]: Stress Test
- generic [ref=e57]: NEW
- generic [ref=e58] [cursor=pointer]:
- generic [ref=e59]: CR
- generic [ref=e60]: Competitor Research
- generic [ref=e61]: NEW
- generic [ref=e62]:
- generic [ref=e63]: Workspace
- generic [ref=e66] [cursor=pointer]: Resources
- generic [ref=e69] [cursor=pointer]: Team Notes
- generic [ref=e72] [cursor=pointer]: Changelog
- generic [ref=e75] [cursor=pointer]: Hướng dẫn
- generic [ref=e76]:
- generic [ref=e77]: Thử nghiệm
- generic [ref=e78] [cursor=pointer]:
- generic [ref=e80]: Text-to-SQL
- generic [ref=e81]: BETA
- generic [ref=e82] [cursor=pointer]:
- generic [ref=e84]: DB Test
- generic [ref=e85]: BETA
- generic [ref=e86] [cursor=pointer]:
- generic [ref=e88]: Feedback Demo
- generic [ref=e89]: NEW
- generic [ref=e90] [cursor=pointer]:
- generic [ref=e92]: Chatbot (Dev)
- generic [ref=e93]: DEV
- generic [ref=e96] [cursor=pointer]: Cache Manager
- generic [ref=e97]:
- generic [ref=e98]: External
- link "API Docs" [ref=e99] [cursor=pointer]:
- /url: /docs
- generic [ref=e101]: API Docs
- link "ReDoc" [ref=e102] [cursor=pointer]:
- /url: /redoc
- generic [ref=e104]: ReDoc
- generic [ref=e106]:
- generic [ref=e107]:
- generic [ref=e108]: U
- generic [ref=e109]: user
- button "⚙" [ref=e110]
- button "⏻" [ref=e111]
- generic [ref=e112]:
- strong [ref=e114]: v2.5.0
- text: · Online
- iframe [ref=e116]:
- generic [ref=f3e2]:
- generic [ref=f3e3]:
- generic [ref=f3e4]:
- heading "Customer Insight" [level=2] [ref=f3e5]
- paragraph [ref=f3e6]: Canifa CDP — Hồ sơ 360° & Dự đoán hành vi
- textbox "Tìm theo tên, email, SĐT..." [ref=f3e7]
- generic [ref=f3e8]:
- generic [ref=f3e9] [cursor=pointer]:
- generic [ref=f3e10]: "N"
- generic [ref=f3e11]:
- generic [ref=f3e12]: Nguyễn Thị Lan
- generic [ref=f3e13]: lan.nguyen@gmail.com
- generic [ref=f3e14]:
- generic [ref=f3e15]: "82"
- generic [ref=f3e16]: Health
- generic [ref=f3e17] [cursor=pointer]:
- generic [ref=f3e18]: T
- generic [ref=f3e19]:
- generic [ref=f3e20]: Trần Văn Minh
- generic [ref=f3e21]: minh.tran@outlook.com
- generic [ref=f3e22]:
- generic [ref=f3e23]: "61"
- generic [ref=f3e24]: Health
- generic [ref=f3e25] [cursor=pointer]:
- generic [ref=f3e26]: L
- generic [ref=f3e27]:
- generic [ref=f3e28]: Lê Phương Anh
- generic [ref=f3e29]: phuonganh.le@yahoo.com
- generic [ref=f3e30]:
- generic [ref=f3e31]: "95"
- generic [ref=f3e32]: Health
- generic [ref=f3e33] [cursor=pointer]:
- generic [ref=f3e34]: P
- generic [ref=f3e35]:
- generic [ref=f3e36]: Phạm Quốc Hùng
- generic [ref=f3e37]: hung.pham@canifa.vn
- generic [ref=f3e38]:
- generic [ref=f3e39]: "35"
- generic [ref=f3e40]: Health
- generic [ref=f3e41] [cursor=pointer]:
- generic [ref=f3e42]: Đ
- generic [ref=f3e43]:
- generic [ref=f3e44]: Đỗ Thanh Hà
- generic [ref=f3e45]: ha.do@gmail.com
- generic [ref=f3e46]:
- generic [ref=f3e47]: "72"
- generic [ref=f3e48]: Health
- generic [ref=f3e49] [cursor=pointer]:
- generic [ref=f3e50]: B
- generic [ref=f3e51]:
- generic [ref=f3e52]: Bùi Đức Thịnh
- generic [ref=f3e53]: thinh.bui@company.com
- generic [ref=f3e54]:
- generic [ref=f3e55]: "88"
- generic [ref=f3e56]: Health
- generic [ref=f3e59]:
- generic [ref=f3e60]: 📊
- paragraph [ref=f3e61]: Chọn một khách hàng
- paragraph [ref=f3e62]: Click vào tên bên trái để xem hồ sơ 360° & AI predictions
\ No newline at end of file
- generic [active] [ref=e1]:
- complementary [ref=e2]:
- generic [ref=e3]:
- generic [ref=e4]: CA
- generic [ref=e5]:
- heading "Canifa AI" [level=2] [ref=e6]
- text: Admin Console
- generic [ref=e7]:
- generic [ref=e8]:
- generic [ref=e9]: Main
- generic [ref=e10] [cursor=pointer]:
- generic [ref=e11]: RM
- generic [ref=e12]: Kế hoạch phát triển
- generic [ref=e15] [cursor=pointer]: Sơ đồ hoạt động
- generic [ref=e16] [cursor=pointer]:
- generic [ref=e18]: Chatbot
- generic [ref=e19]: LIVE
- generic [ref=e22] [cursor=pointer]: History
- generic [ref=e25] [cursor=pointer]: Product Perf.
- generic [ref=e26] [cursor=pointer]:
- generic [ref=e28]: AI Data Analyst
- generic [ref=e29]: NEW
- generic [ref=e30] [cursor=pointer]:
- generic [ref=e31]: SQL
- generic [ref=e32]: AI sinh SQL
- generic [ref=e33]: NEW
- generic [ref=e34] [cursor=pointer]:
- generic [ref=e35]: LIVE
- generic [ref=e36]: Realtime Monitor
- generic [ref=e37]: LIVE
- generic [ref=e38] [cursor=pointer]:
- generic [ref=e39]: PO
- generic [ref=e40]: Prompt Optimizer
- generic [ref=e41]: NEW
- generic [ref=e42] [cursor=pointer]:
- generic [ref=e43]: US
- generic [ref=e44]: User Simulator
- generic [ref=e45]: NEW
- generic [ref=e46] [cursor=pointer]:
- generic [ref=e47]: UI
- generic [ref=e48]: User Insight
- generic [ref=e49]: NEW
- generic [ref=e50] [cursor=pointer]:
- generic [ref=e51]: RT
- generic [ref=e52]: Regression Test
- generic [ref=e53]: NEW
- generic [ref=e54] [cursor=pointer]:
- generic [ref=e55]: ST
- generic [ref=e56]: Stress Test
- generic [ref=e57]: NEW
- generic [ref=e58] [cursor=pointer]:
- generic [ref=e59]: CR
- generic [ref=e60]: Competitor Research
- generic [ref=e61]: NEW
- generic [ref=e62]:
- generic [ref=e63]: Workspace
- generic [ref=e66] [cursor=pointer]: Resources
- generic [ref=e69] [cursor=pointer]: Team Notes
- generic [ref=e72] [cursor=pointer]: Changelog
- generic [ref=e75] [cursor=pointer]: Hướng dẫn
- generic [ref=e76]:
- generic [ref=e77]: Thử nghiệm
- generic [ref=e78] [cursor=pointer]:
- generic [ref=e80]: Text-to-SQL
- generic [ref=e81]: BETA
- generic [ref=e82] [cursor=pointer]:
- generic [ref=e84]: DB Test
- generic [ref=e85]: BETA
- generic [ref=e86] [cursor=pointer]:
- generic [ref=e88]: Feedback Demo
- generic [ref=e89]: NEW
- generic [ref=e90] [cursor=pointer]:
- generic [ref=e92]: Chatbot (Dev)
- generic [ref=e93]: DEV
- generic [ref=e96] [cursor=pointer]: Cache Manager
- generic [ref=e97]:
- generic [ref=e98]: External
- link "API Docs" [ref=e99] [cursor=pointer]:
- /url: /docs
- generic [ref=e101]: API Docs
- link "ReDoc" [ref=e102] [cursor=pointer]:
- /url: /redoc
- generic [ref=e104]: ReDoc
- generic [ref=e106]:
- generic [ref=e107]:
- generic [ref=e108]: U
- generic [ref=e109]: user
- button "⚙" [ref=e110]
- button "⏻" [ref=e111]
- generic [ref=e112]:
- strong [ref=e114]: v2.5.0
- text: · Online
- iframe [ref=e116]:
- generic [ref=f4e2]:
- generic [ref=f4e3]:
- generic [ref=f4e4]:
- generic [ref=f4e5]: US
- generic [ref=f4e6]:
- paragraph [ref=f4e7]: User Simulator
- paragraph [ref=f4e8]: AI persona test chatbot
- generic [ref=f4e9]:
- button "Setup" [ref=f4e10] [cursor=pointer]
- button "Simulate" [ref=f4e11] [cursor=pointer]
- button "Matrix" [ref=f4e12] [cursor=pointer]
- button "Insights" [ref=f4e13] [cursor=pointer]
- button "Run All (5)" [ref=f4e15] [cursor=pointer]
- generic [ref=f4e18]:
- generic [ref=f4e19]:
- generic [ref=f4e20]:
- generic [ref=f4e21]:
- paragraph [ref=f4e22]: Chatbot Prompt
- paragraph [ref=f4e23]: Prompt chatbot hiện tại — personas sẽ test với cái này
- button "Edit" [ref=f4e24] [cursor=pointer]
- generic [ref=f4e25]: Ban la tro ly tu van thoi trang Canifa. Hay tra loi cau hoi cua khach hang mot cach than thien va huu ich.
- generic [ref=f4e26]:
- generic [ref=f4e27]:
- paragraph [ref=f4e28]: User Personas (5)
- paragraph [ref=f4e29]: AI se dong vai tung persona de test chatbot
- button "+ Add Persona" [ref=f4e30] [cursor=pointer]
- generic [ref=f4e31]:
- generic [ref=f4e32]:
- generic [ref=f4e33]:
- generic [ref=f4e35]: C
- generic [ref=f4e36]:
- paragraph [ref=f4e37]: Chi Lan
- paragraph [ref=f4e38]: 40 tuoi - Non-tech user
- paragraph [ref=f4e39]: Khong quen dung app, can huong dan tung buoc. Hay bo cuoc neu khong hieu.
- generic [ref=f4e40]:
- button "Edit" [ref=f4e41] [cursor=pointer]
- button "x" [ref=f4e42] [cursor=pointer]
- button "▶" [ref=f4e43] [cursor=pointer]
- generic [ref=f4e44]:
- generic [ref=f4e45]:
- generic [ref=f4e47]: D
- generic [ref=f4e48]:
- paragraph [ref=f4e49]: Dev Minh
- paragraph [ref=f4e50]: 28 tuoi - Technical power user
- paragraph [ref=f4e51]: Developer, hoi ky thuat chi tiet, hay test edge case, phan bac neu cau tra loi khong chinh xac.
- generic [ref=f4e52]:
- button "Edit" [ref=f4e53] [cursor=pointer]
- button "x" [ref=f4e54] [cursor=pointer]
- button "▶" [ref=f4e55] [cursor=pointer]
- generic [ref=f4e56]:
- generic [ref=f4e57]:
- generic [ref=f4e59]: S
- generic [ref=f4e60]:
- paragraph [ref=f4e61]: SV Nam
- paragraph [ref=f4e62]: 20 tuoi - Budget shopper
- paragraph [ref=f4e63]: Ngan sach han che, hay so sanh doi thu, nhay cam ve gia, thich mac ca va tim deal.
- generic [ref=f4e64]:
- button "Edit" [ref=f4e65] [cursor=pointer]
- button "x" [ref=f4e66] [cursor=pointer]
- button "▶" [ref=f4e67] [cursor=pointer]
- generic [ref=f4e68]:
- generic [ref=f4e69]:
- generic [ref=f4e71]: A
- generic [ref=f4e72]:
- paragraph [ref=f4e73]: Anh Tuan
- paragraph [ref=f4e74]: 45 tuoi - Busy executive
- paragraph [ref=f4e75]: Giam doc, rat ban, muon cau tra loi ngan gon va nhanh. Ghet phai doc nhieu.
- generic [ref=f4e76]:
- button "Edit" [ref=f4e77] [cursor=pointer]
- button "x" [ref=f4e78] [cursor=pointer]
- button "▶" [ref=f4e79] [cursor=pointer]
- generic [ref=f4e80]:
- generic [ref=f4e81]:
- generic [ref=f4e83]: B
- generic [ref=f4e84]:
- paragraph [ref=f4e85]: Ba Hoa
- paragraph [ref=f4e86]: 65 tuoi - Elderly user
- paragraph [ref=f4e87]: Lon tuoi, dung dien thoai kho, can giai thich cham tung buoc, khong hieu tu chuyen nganh.
- generic [ref=f4e88]:
- button "Edit" [ref=f4e89] [cursor=pointer]
- button "x" [ref=f4e90] [cursor=pointer]
- button "▶" [ref=f4e91] [cursor=pointer]
\ No newline at end of file
- generic [active] [ref=e1]:
- complementary [ref=e2]:
- generic [ref=e3]:
- generic [ref=e4]: CA
- generic [ref=e5]:
- heading "Canifa AI" [level=2] [ref=e6]
- text: Admin Console
- generic [ref=e7]:
- generic [ref=e8]:
- generic [ref=e9]: Main
- generic [ref=e10] [cursor=pointer]:
- generic [ref=e11]: RM
- generic [ref=e12]: Kế hoạch phát triển
- generic [ref=e15] [cursor=pointer]: Sơ đồ hoạt động
- generic [ref=e16] [cursor=pointer]:
- generic [ref=e18]: Chatbot
- generic [ref=e19]: LIVE
- generic [ref=e22] [cursor=pointer]: History
- generic [ref=e25] [cursor=pointer]: Product Perf.
- generic [ref=e26] [cursor=pointer]:
- generic [ref=e28]: AI Data Analyst
- generic [ref=e29]: NEW
- generic [ref=e30] [cursor=pointer]:
- generic [ref=e31]: SQL
- generic [ref=e32]: AI sinh SQL
- generic [ref=e33]: NEW
- generic [ref=e34] [cursor=pointer]:
- generic [ref=e35]: LIVE
- generic [ref=e36]: Realtime Monitor
- generic [ref=e37]: LIVE
- generic [ref=e38] [cursor=pointer]:
- generic [ref=e39]: PO
- generic [ref=e40]: Prompt Optimizer
- generic [ref=e41]: NEW
- generic [ref=e42] [cursor=pointer]:
- generic [ref=e43]: US
- generic [ref=e44]: User Simulator
- generic [ref=e45]: NEW
- generic [ref=e46] [cursor=pointer]:
- generic [ref=e47]: UI
- generic [ref=e48]: User Insight
- generic [ref=e49]: NEW
- generic [ref=e50] [cursor=pointer]:
- generic [ref=e51]: RT
- generic [ref=e52]: Regression Test
- generic [ref=e53]: NEW
- generic [ref=e54] [cursor=pointer]:
- generic [ref=e55]: ST
- generic [ref=e56]: Stress Test
- generic [ref=e57]: NEW
- generic [ref=e58] [cursor=pointer]:
- generic [ref=e59]: CR
- generic [ref=e60]: Competitor Research
- generic [ref=e61]: NEW
- generic [ref=e62]:
- generic [ref=e63]: Workspace
- generic [ref=e66] [cursor=pointer]: Resources
- generic [ref=e69] [cursor=pointer]: Team Notes
- generic [ref=e72] [cursor=pointer]: Changelog
- generic [ref=e75] [cursor=pointer]: Hướng dẫn
- generic [ref=e76]:
- generic [ref=e77]: Thử nghiệm
- generic [ref=e78] [cursor=pointer]:
- generic [ref=e80]: Text-to-SQL
- generic [ref=e81]: BETA
- generic [ref=e82] [cursor=pointer]:
- generic [ref=e84]: DB Test
- generic [ref=e85]: BETA
- generic [ref=e86] [cursor=pointer]:
- generic [ref=e88]: Feedback Demo
- generic [ref=e89]: NEW
- generic [ref=e90] [cursor=pointer]:
- generic [ref=e92]: Chatbot (Dev)
- generic [ref=e93]: DEV
- generic [ref=e96] [cursor=pointer]: Cache Manager
- generic [ref=e97]:
- generic [ref=e98]: External
- link "API Docs" [ref=e99] [cursor=pointer]:
- /url: /docs
- generic [ref=e101]: API Docs
- link "ReDoc" [ref=e102] [cursor=pointer]:
- /url: /redoc
- generic [ref=e104]: ReDoc
- generic [ref=e106]:
- generic [ref=e107]:
- generic [ref=e108]: U
- generic [ref=e109]: user
- button "⚙" [ref=e110]
- button "⏻" [ref=e111]
- generic [ref=e112]:
- strong [ref=e114]: v2.5.0
- text: · Online
- iframe [ref=e116]:
- generic [ref=f5e2]:
- generic [ref=f5e4]:
- heading "💬 History Viewer" [level=1] [ref=f5e5]
- paragraph [ref=f5e6]: Xem lịch sử hội thoại theo identity key
- generic [ref=f5e7]:
- generic [ref=f5e8]: Identity Key
- textbox "device_id hoặc user_id..." [ref=f5e9]
- generic [ref=f5e10]: Từ
- textbox [ref=f5e11]
- generic [ref=f5e12]: Đến
- textbox [ref=f5e13]
- generic [ref=f5e14]:
- button "Fetch" [ref=f5e15] [cursor=pointer]
- button "Hôm nay" [ref=f5e16] [cursor=pointer]
- button "Clear" [ref=f5e17] [cursor=pointer]
- paragraph [ref=f5e22]:
- text: Nhập identity key và nhấn
- strong [ref=f5e23]: Fetch
- text: để xem lịch sử
\ No newline at end of file
- generic [ref=e2]: "{\"detail\":\"Not Found\"}"
\ No newline at end of file
"""
Experiment Log API — AI version experiment tracking.
"""
import logging
import uuid
from datetime import datetime, timedelta, timezone
from typing import Optional, List
from collections import defaultdict
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from api.notes_route import _get_pool, _now
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/dashboard", tags=["Experiment Log"])
async def _query(q: str, params: tuple = ()) -> list[dict]:
pool = await _get_pool()
async with pool.connection() as conn:
async with conn.cursor() as cur:
await cur.execute(q, params)
cols = [d[0] for d in cur.description] if cur.description else []
rows = await cur.fetchall()
return [dict(zip(cols, row)) for row in rows]
async def _execute(q: str, params: tuple = ()):
pool = await _get_pool()
async with pool.connection() as conn:
async with conn.cursor() as cur:
await cur.execute(q, params)
await conn.commit()
def _serialize(row: dict) -> dict:
r = {**row}
for k in ("created_at", "updated_at"):
if k in r and r[k]:
r[k] = r[k].isoformat()
# Convert Decimal to float
for k in ("score", "latency_avg", "cost_per_req", "error_rate"):
if k in r and r[k] is not None:
r[k] = float(r[k])
return r
# ── Models ──
class ExperimentCreate(BaseModel):
version: str
title: str
content: str = ""
tags: List[str] = []
score: Optional[float] = None
latency_avg: Optional[float] = None
cost_per_req: Optional[float] = None
error_rate: Optional[float] = None
model_name: str = ""
status: str = "draft"
pinned: bool = False
class ExperimentUpdate(BaseModel):
version: Optional[str] = None
title: Optional[str] = None
content: Optional[str] = None
tags: Optional[List[str]] = None
score: Optional[float] = None
latency_avg: Optional[float] = None
cost_per_req: Optional[float] = None
error_rate: Optional[float] = None
model_name: Optional[str] = None
status: Optional[str] = None
pinned: Optional[bool] = None
# ── Endpoints ──
@router.get("/experiments")
async def list_experiments(tag: Optional[str] = None, status: Optional[str] = None):
"""List all experiments. Optional filter by tag or status."""
q = "SELECT * FROM experiment_log WHERE 1=1"
params = []
if tag:
q += " AND %s = ANY(tags)"
params.append(tag)
if status:
q += " AND status = %s"
params.append(status)
q += " ORDER BY pinned DESC, created_at DESC"
rows = await _query(q, tuple(params))
items = [_serialize(r) for r in rows]
# Collect all tags with counts
tag_counts = defaultdict(int)
all_rows = await _query("SELECT tags FROM experiment_log")
for r in all_rows:
for t in (r.get("tags") or []):
tag_counts[t] += 1
# Activity heatmap: date → count
activity_rows = await _query(
"SELECT DATE(created_at AT TIME ZONE 'Asia/Ho_Chi_Minh') as d, COUNT(*) as c FROM experiment_log GROUP BY d"
)
activity = {str(r["d"]): int(r["c"]) for r in activity_rows}
return {
"status": "success",
"experiments": items,
"total": len(items),
"tags": dict(sorted(tag_counts.items(), key=lambda x: -x[1])),
"activity": activity,
}
@router.post("/experiments")
async def create_experiment(item: ExperimentCreate):
"""Create a new experiment entry."""
eid = str(uuid.uuid4())[:8]
now = _now()
await _execute(
"""INSERT INTO experiment_log
(id, version, title, content, tags, score, latency_avg, cost_per_req, error_rate, model_name, status, pinned, author, created_at, updated_at)
VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,'Admin',%s,%s)""",
(eid, item.version, item.title, item.content, item.tags,
item.score, item.latency_avg, item.cost_per_req, item.error_rate,
item.model_name, item.status, item.pinned, now, now),
)
return {"status": "success", "id": eid}
@router.put("/experiments/{exp_id}")
async def update_experiment(exp_id: str, update: ExperimentUpdate):
"""Update an experiment."""
sets, vals = [], []
for field in ("version", "title", "content", "score", "latency_avg",
"cost_per_req", "error_rate", "model_name", "status", "pinned"):
val = getattr(update, field, None)
if val is not None:
sets.append(f"{field} = %s")
vals.append(val)
if update.tags is not None:
sets.append("tags = %s")
vals.append(update.tags)
if not sets:
raise HTTPException(status_code=400, detail="Nothing to update")
sets.append("updated_at = %s")
vals.append(_now())
vals.append(exp_id)
await _execute(f"UPDATE experiment_log SET {', '.join(sets)} WHERE id = %s", tuple(vals))
return {"status": "success"}
@router.delete("/experiments/{exp_id}")
async def delete_experiment(exp_id: str):
"""Delete an experiment."""
await _execute("DELETE FROM experiment_log WHERE id = %s", (exp_id,))
return {"status": "success"}
@router.patch("/experiments/{exp_id}/pin")
async def toggle_experiment_pin(exp_id: str):
"""Toggle pin status."""
rows = await _query("SELECT pinned FROM experiment_log WHERE id = %s", (exp_id,))
if not rows:
raise HTTPException(status_code=404, detail="Not found")
new_pin = not rows[0]["pinned"]
await _execute("UPDATE experiment_log SET pinned = %s, updated_at = %s WHERE id = %s",
(new_pin, _now(), exp_id))
return {"status": "success", "pinned": new_pin}
@router.get("/experiments/compare")
async def compare_experiments(id1: str, id2: str):
"""Compare two experiments side-by-side."""
rows = await _query("SELECT * FROM experiment_log WHERE id IN (%s, %s)", (id1, id2))
if len(rows) < 2:
raise HTTPException(status_code=404, detail="One or both experiments not found")
items = [_serialize(r) for r in rows]
return {"status": "success", "experiments": items}
import logging
from fastapi import APIRouter, Body
from common.message_limit import message_limit_service
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/limit", tags=["Limit Management"])
@router.get("/info", summary="Lấy thông tin limit của 1 ID (vd: device:xxx hoặc user:yyy)")
async def get_limit_info(identity_key: str):
"""
identity_key format: 'device:id' hoặc 'user:id'
"""
is_authenticated = identity_key.startswith("user:")
info = await message_limit_service.get_usage(identity_key, is_authenticated)
return {"status": "success", "info": info}
@router.post("/reset", summary="Reset limit (thêm limit / cho phép nhắn lại) cho 1 ID")
async def reset_limit(payload: dict = Body(...)):
identity_key = payload.get("identity_key")
if not identity_key:
return {"status": "error", "message": "Missing identity_key"}
success = await message_limit_service.reset(identity_key)
if success:
return {"status": "success", "message": f"Đã reset/thêm limit cho {identity_key} (về 0)"}
else:
return {"status": "error", "message": "Có lỗi khi reset limit"}
This diff is collapsed.
This diff is collapsed.
"""
Roadmap & Flow API — data from Postgres tables.
"""
import logging
import uuid
from datetime import datetime, timedelta, timezone
from typing import Optional
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from config import CHECKPOINT_POSTGRES_URL
from api.notes_route import _get_pool, _now
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/dashboard", tags=["Roadmap & Flow"])
# ═══════════════════════════════════════════════════
# ROADMAP API
# ═══════════════════════════════════════════════════
class RoadmapCreate(BaseModel):
version: str
title: str
description: str = ""
status: str = "planned"
badge: str = "planned"
target_date: str = ""
sort_order: int = 0
class RoadmapUpdate(BaseModel):
version: Optional[str] = None
title: Optional[str] = None
description: Optional[str] = None
status: Optional[str] = None
badge: Optional[str] = None
target_date: Optional[str] = None
sort_order: Optional[int] = None
async def _query(q: str, params: tuple = ()) -> list[dict]:
pool = await _get_pool()
async with pool.connection() as conn:
async with conn.cursor() as cur:
await cur.execute(q, params)
cols = [d[0] for d in cur.description] if cur.description else []
rows = await cur.fetchall()
return [dict(zip(cols, row)) for row in rows]
async def _execute(q: str, params: tuple = ()):
pool = await _get_pool()
async with pool.connection() as conn:
async with conn.cursor() as cur:
await cur.execute(q, params)
await conn.commit()
def _serialize(row: dict) -> dict:
r = {**row}
for k in ("created_at", "updated_at"):
if k in r and r[k]:
r[k] = r[k].isoformat()
return r
@router.get("/roadmap")
async def list_roadmap():
"""Get all roadmap items grouped by version."""
rows = await _query(
"SELECT * FROM roadmap_items ORDER BY version DESC, sort_order ASC"
)
items = [_serialize(r) for r in rows]
# Group by version
grouped = {}
for item in items:
v = item["version"]
if v not in grouped:
grouped[v] = {"version": v, "badge": item.get("badge", "planned"), "target_date": item.get("target_date", ""), "items": []}
grouped[v]["items"].append(item)
return {"status": "success", "versions": list(grouped.values()), "total": len(items)}
@router.post("/roadmap")
async def create_roadmap_item(item: RoadmapCreate):
"""Add a new roadmap item."""
rid = str(uuid.uuid4())[:8]
now = _now()
await _execute(
"""INSERT INTO roadmap_items (id, version, title, description, status, badge, target_date, sort_order, created_at, updated_at)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
(rid, item.version, item.title, item.description, item.status, item.badge, item.target_date, item.sort_order, now, now),
)
return {"status": "success", "id": rid}
@router.put("/roadmap/{item_id}")
async def update_roadmap_item(item_id: str, update: RoadmapUpdate):
"""Update a roadmap item."""
sets, vals = [], []
for field in ("version", "title", "description", "status", "badge", "target_date", "sort_order"):
val = getattr(update, field, None)
if val is not None:
sets.append(f"{field} = %s")
vals.append(val)
if not sets:
raise HTTPException(status_code=400, detail="Nothing to update")
sets.append("updated_at = %s")
vals.append(_now())
vals.append(item_id)
await _execute(f"UPDATE roadmap_items SET {', '.join(sets)} WHERE id = %s", tuple(vals))
return {"status": "success"}
@router.delete("/roadmap/{item_id}")
async def delete_roadmap_item(item_id: str):
"""Delete a roadmap item."""
await _execute("DELETE FROM roadmap_items WHERE id = %s", (item_id,))
return {"status": "success"}
# ═══════════════════════════════════════════════════
# FLOW API
# ═══════════════════════════════════════════════════
class FlowCreate(BaseModel):
title: str
description: str = ""
category: str = "general"
parent_id: Optional[str] = None
sort_order: int = 0
icon: str = ""
class FlowUpdate(BaseModel):
title: Optional[str] = None
description: Optional[str] = None
category: Optional[str] = None
parent_id: Optional[str] = None
sort_order: Optional[int] = None
icon: Optional[str] = None
status: Optional[str] = None
@router.get("/flow")
async def list_flow():
"""Get all flow items ordered by sort_order."""
rows = await _query("SELECT * FROM flow_items ORDER BY sort_order ASC")
items = [_serialize(r) for r in rows]
return {"status": "success", "items": items, "total": len(items)}
@router.post("/flow")
async def create_flow_item(item: FlowCreate):
"""Add a new flow item."""
fid = str(uuid.uuid4())[:8]
now = _now()
await _execute(
"""INSERT INTO flow_items (id, title, description, category, parent_id, sort_order, icon, created_at, updated_at)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)""",
(fid, item.title, item.description, item.category, item.parent_id, item.sort_order, item.icon, now, now),
)
return {"status": "success", "id": fid}
@router.put("/flow/{item_id}")
async def update_flow_item(item_id: str, update: FlowUpdate):
"""Update a flow item."""
sets, vals = [], []
for field in ("title", "description", "category", "parent_id", "sort_order", "icon", "status"):
val = getattr(update, field, None)
if val is not None:
sets.append(f"{field} = %s")
vals.append(val)
if not sets:
raise HTTPException(status_code=400, detail="Nothing to update")
sets.append("updated_at = %s")
vals.append(_now())
vals.append(item_id)
await _execute(f"UPDATE flow_items SET {', '.join(sets)} WHERE id = %s", tuple(vals))
return {"status": "success"}
@router.delete("/flow/{item_id}")
async def delete_flow_item(item_id: str):
"""Delete a flow item."""
await _execute("DELETE FROM flow_items WHERE id = %s", (item_id,))
return {"status": "success"}
......@@ -24,7 +24,7 @@ async def get_current_user(request: Request) -> dict:
raise HTTPException(status_code=401, detail="Token expired or invalid")
user_id = int(claims["sub"])
conn = get_conn()
try:
cur = conn.cursor()
......
......@@ -16,8 +16,8 @@ class CanifaDbPool:
try:
self._pool = ConnectionPool(
CHECKPOINT_POSTGRES_URL,
min_size=5,
max_size=40,
min_size=2,
max_size=15,
max_idle=300,
timeout=10,
kwargs={"autocommit": True}
......
This diff is collapsed.
[
{
"id": "exp_chatbot_prod",
"name": "Chatbot (Production)",
"url": "http://172.16.2.207:5005/static/index.html",
"icon": "💬",
"badge": null,
"badge_type": null,
"section": "main",
"pinned": true,
"description": "Chatbot AI Stylist Canifa — phiên bản production đang chạy trên server 172.16.2.207. Đây là bản ổn định dùng cho team test và demo.",
"versions": [
{ "version": "v5.10", "date": "2026-03-11", "author": "Dev", "note": "Phiên bản ổn định, đã deploy production" }
]
},
{
"id": "exp_1",
"name": "Text-to-SQL",
"url": "/static/test_sql.html",
"icon": "🗄️",
"badge": "BETA",
"badge_type": "beta",
"pinned": true,
"description": "Cho phép hỏi database bằng tiếng Việt, tự convert thành SQL query và trả kết quả trực tiếp.",
"versions": [
{ "version": "v0.1", "date": "2026-03-08", "author": "Dev", "note": "Bản đầu tiên — hỗ trợ SELECT cơ bản" }
]
},
{
"id": "exp_2",
"name": "DB Test",
"url": "/static/test_db.html",
"icon": "🔍",
"badge": "BETA",
"badge_type": "beta",
"pinned": true,
"description": "Test trực tiếp các truy vấn database, xem dữ liệu sản phẩm và stock.",
"versions": [
{ "version": "v0.1", "date": "2026-03-08", "author": "Dev", "note": "Bản đầu tiên — query trực tiếp" }
]
},
{
"id": "exp_3",
"name": "Feedback Demo",
"url": "/static/feedback_demo.html",
"icon": "📝",
"badge": "NEW",
"badge_type": "new",
"pinned": true,
"description": "Demo hệ thống feedback Like/Dislike/Complaint cho chatbot, phân loại tự động bằng AI.",
"versions": [
{ "version": "v0.1", "date": "2026-03-10", "author": "Dev", "note": "Bản đầu tiên — Like/Dislike + Complaint form" }
]
},
{
"id": "exp_chatbot_dev",
"name": "Chatbot (Dev)",
"url": "/static/index.html",
"icon": "💬",
"badge": "DEV",
"badge_type": "dev",
"pinned": true,
"description": "Phiên bản dev của Chatbot, chạy trên localhost. Dùng để test tính năng mới trước khi deploy lên production.",
"versions": [
{ "version": "v5.10", "date": "2026-03-11", "author": "Dev", "note": "Version hiện tại — dev localhost" }
]
}
{
"id": "exp_chatbot_prod",
"name": "Chatbot (Production)",
"url": "http://172.16.2.207:5000/static/index.html",
"icon": "💬",
"badge": null,
"badge_type": null,
"section": "main",
"pinned": true,
"description": "Chatbot AI Stylist Canifa — phiên bản production đang chạy trên server 172.16.2.207. Đây là bản ổn định dùng cho team test và demo.",
"versions": [
{
"version": "v5.10",
"date": "2026-03-11",
"author": "Dev",
"note": "Phiên bản ổn định, đã deploy production"
}
]
},
{
"id": "exp_1",
"name": "Text-to-SQL",
"url": "/static/test_sql.html",
"icon": "🗄️",
"badge": "BETA",
"badge_type": "beta",
"pinned": true,
"description": "Cho phép hỏi database bằng tiếng Việt, tự convert thành SQL query và trả kết quả trực tiếp.",
"versions": [
{
"version": "v0.1",
"date": "2026-03-08",
"author": "Dev",
"note": "Bản đầu tiên — hỗ trợ SELECT cơ bản"
}
]
},
{
"id": "exp_2",
"name": "DB Test",
"url": "/static/test_db.html",
"icon": "🔍",
"badge": "BETA",
"badge_type": "beta",
"pinned": true,
"description": "Test trực tiếp các truy vấn database, xem dữ liệu sản phẩm và stock.",
"versions": [
{
"version": "v0.1",
"date": "2026-03-08",
"author": "Dev",
"note": "Bản đầu tiên — query trực tiếp"
}
]
},
{
"id": "exp_3",
"name": "Feedback Demo",
"url": "/static/feedback_demo.html",
"icon": "📝",
"badge": "NEW",
"badge_type": "new",
"pinned": true,
"description": "Demo hệ thống feedback Like/Dislike/Complaint cho chatbot, phân loại tự động bằng AI.",
"versions": [
{
"version": "v0.1",
"date": "2026-03-10",
"author": "Dev",
"note": "Bản đầu tiên — Like/Dislike + Complaint form"
}
]
},
{
"id": "exp_chatbot_dev",
"name": "Chatbot (Dev)",
"url": "/static/index.html",
"icon": "💬",
"badge": "DEV",
"badge_type": "dev",
"pinned": true,
"description": "Phiên bản dev của Chatbot, chạy trên localhost. Dùng để test tính năng mới trước khi deploy lên production.",
"versions": [
{
"version": "v5.10",
"date": "2026-03-11",
"author": "Dev",
"note": "Version hiện tại — dev localhost"
}
]
}
]
\ No newline at end of file
name: canifa-feedback
services:
# --- Backend Service ---
backend:
build: .
build:
context: .
dockerfile: Dockerfile.dev
container_name: canifa_backend
env_file: .env
ports:
- "5005:5000"
- "5006:5000"
volumes:
- .:/app
environment:
- PORT=5000
- PORT=5006
restart: unless-stopped
deploy:
resources:
limits:
memory: 8g
networks:
- backend_network
- canifa_feedback_network
logging:
driver: "json-file"
options:
tag: "{{.Name}}"
networks:
backend_network:
canifa_feedback_network:
driver: bridge
ipam:
driver: default
......
"""Seed flow_items with proper UTF-8 via HTTP API."""
import urllib.request, json
BASE = "http://localhost:5000/api/dashboard/flow"
# Delete all existing
resp = urllib.request.urlopen(BASE)
data = json.loads(resp.read())
for item in data.get("items", []):
req = urllib.request.Request(f"{BASE}/{item['id']}", method="DELETE")
urllib.request.urlopen(req)
print("Cleared all existing flow items")
items = [
{"id":"f01","title":"Kh\u00e1ch g\u1eedi tin nh\u1eafn","description":"Kh\u00e1ch h\u00e0ng nh\u1eadp c\u00e2u h\u1ecfi qua chatbot widget tr\u00ean web/app","category":"input","parent_id":None,"sort_order":1,"icon":"\ud83d\udcac"},
{"id":"f02","title":"API Gateway","description":"FastAPI nh\u1eadn request, x\u00e1c th\u1ef1c, rate limit","category":"process","parent_id":None,"sort_order":2,"icon":"\ud83d\udd0c"},
{"id":"f03","title":"Ki\u1ec3m tra cache","description":"Tra b\u1ed9 nh\u1edb t\u1ea1m \u2014 c\u00e2u h\u1ecfi n\u00e0y \u0111\u00e3 \u0111\u01b0\u1ee3c h\u1ecfi ch\u01b0a?","category":"process","parent_id":None,"sort_order":3,"icon":"\u26a1"},
{"id":"f04","title":"L\u1ea5y l\u1ecbch s\u1eed + h\u1ed3 s\u01a1 kh\u00e1ch","description":"10 tin nh\u1eafn g\u1ea7n nh\u1ea5t + h\u1ed3 s\u01a1 d\u00e0i h\u1ea1n (size, s\u1edf th\u00edch)","category":"process","parent_id":None,"sort_order":4,"icon":"\ud83d\udcbe"},
{"id":"f05","title":"AI Agent suy ngh\u0129","description":"Gemini ph\u00e2n t\u00edch \u00fd \u0111\u1ecbnh, quy\u1ebft \u0111\u1ecbnh c\u1ea7n tra c\u1ee9u g\u00ec","category":"process","parent_id":None,"sort_order":5,"icon":"\ud83e\udde0"},
{"id":"f06","title":"Product Search (Vector)","description":"T\u00ecm s\u1ea3n ph\u1ea9m b\u1eb1ng semantic search + SQL filters","category":"tool","parent_id":"f05","sort_order":6,"icon":"\ud83d\udd0d"},
{"id":"f07","title":"Stock Check","description":"Ki\u1ec3m tra t\u1ed3n kho realtime qua API Magento","category":"tool","parent_id":"f05","sort_order":7,"icon":"\ud83d\udce6"},
{"id":"f08","title":"Knowledge Base","description":"Tra ch\u00ednh s\u00e1ch \u0111\u1ed5i tr\u1ea3, khuy\u1ebfn m\u00e3i, th\u00f4ng tin c\u1eeda h\u00e0ng","category":"tool","parent_id":"f05","sort_order":8,"icon":"\ud83d\udcda"},
{"id":"f09","title":"Streaming Response","description":"Tr\u1ea3 l\u1eddi t\u1eebng ch\u1eef nh\u01b0 ChatGPT, k\u00e8m product cards","category":"output","parent_id":None,"sort_order":9,"icon":"\ud83d\udce1"},
{"id":"f10","title":"Background Tasks","description":"L\u01b0u l\u1ecbch s\u1eed, c\u1eadp nh\u1eadt h\u1ed3 s\u01a1 kh\u00e1ch, cache, log Langfuse","category":"output","parent_id":None,"sort_order":10,"icon":"\u2699\ufe0f"},
{"id":"f11","title":"Langfuse Trace","description":"Log observability: latency, tokens, cost, scores","category":"monitor","parent_id":None,"sort_order":11,"icon":"\ud83d\udcca"},
]
for item in items:
body = json.dumps(item).encode("utf-8")
req = urllib.request.Request(BASE, data=body, headers={"Content-Type":"application/json"}, method="POST")
urllib.request.urlopen(req)
print(f"Seeded {len(items)} flow items OK")
"""Seed roadmap notes via the notes API."""
import urllib.request, json, time
BASE = "http://localhost:5000/api/dashboard/notes"
notes = [
{"title": "v1.0 \u2014 Production", "content": """Model: GPT-4.1-mini
Search: Vector search (pgvector, IVFFlat)
Stock: API Magento realtime
Prompt: Module v1 (persona, guardrails, sales flow)
Observability: Langfuse tracing + scoring
Chat: L\u01b0u l\u1ecbch s\u1eed + Like/Dislike feedback""", "category": "note", "pinned": True},
{"title": "v1.1 \u2014 \u0110\u1ed5i model (th\u1eed nghi\u1ec7m)", "content": """Chuy\u1ec3n GPT-4.1-mini \u2192 Gemini 3.1 Flash-Lite
Gi\u1ea3m ~55% chi ph\u00ed per request
R\u00fat g\u1ecdn prompt 56%, format ph\u00f9 h\u1ee3p Gemini
Benchmark: 20 test cases so s\u00e1nh Score/Latency/Cost
C\u01a1 ch\u1ebf rollback v\u1ec1 GPT n\u1ebfu Gemini kh\u00f4ng \u1ed5n""", "category": "note", "pinned": True},
{"title": "v1.2 \u2014 \u0110\u1ed5i logic l\u1ea5y d\u1eef li\u1ec7u (th\u1eed nghi\u1ec7m)", "content": """Hybrid Search: vector + keyword thay v\u00ec vector-only
HNSW Index thay IVFFlat \u2192 recall t\u0103ng ~15%
Filters n\u00e2ng cao: l\u1ecdc gi\u00e1, m\u00e0u, size, danh m\u1ee5c tr\u01b0\u1edbc khi search
Caching layer (Redis): cache k\u1ebft qu\u1ea3 search ph\u1ed5 bi\u1ebfn, gi\u1ea3m latency""", "category": "note", "pinned": True},
{"title": "v2.0 \u2014 N\u00e2ng c\u1ea5p l\u1edbn (k\u1ebf ho\u1ea1ch)", "content": """Prompt Engineering v2: rewrite to\u00e0n b\u1ed9, multi-turn context, persona n\u00e2ng cao
Giao di\u1ec7n chatbot m\u1edbi: dark mode, product cards, quick replies
Multi-Agent: Sales Agent + Support Agent + Analytics Agent ph\u1ed1i h\u1ee3p
A/B Testing Engine: so s\u00e1nh prompt/model variants tr\u00ean live traffic
Customer Analytics Dashboard: ph\u00e2n t\u00edch h\u00e0nh vi kh\u00e1ch h\u00e0ng t\u1eeb chat data""", "category": "note", "pinned": True},
]
for note in notes:
body = json.dumps(note).encode("utf-8")
req = urllib.request.Request(BASE, data=body, headers={"Content-Type":"application/json"}, method="POST")
urllib.request.urlopen(req)
time.sleep(0.3)
print(f"Seeded {len(notes)} roadmap notes OK")
......@@ -31,7 +31,10 @@ from api.prompt_optimizer_route import router as prompt_optimizer_router
from api.user_simulator_route import router as user_simulator_router
from api.regression_test_route import router as regression_test_router
from api.stress_test_route import router as stress_test_router
from api.roadmap_flow_route import router as roadmap_flow_router
from api.experiment_log_route import router as experiment_log_router
from api.auth_route import router as auth_router
from api.product_desc_route import router as product_desc_router
from common.cache import redis_cache
from common.middleware import middleware_manager
from config import PORT, REDIS_CACHE_TURN_ON
......@@ -79,7 +82,7 @@ async def startup_event():
@app.get("/")
async def root():
return RedirectResponse(url="/static/main.html")
return RedirectResponse(url="/static/main.html?page=roadmap.html")
@app.get("/health")
async def health():
......@@ -89,8 +92,8 @@ async def health():
@app.get("/static/{file_path:path}")
async def serve_static(file_path: str):
"""Serve static files explicitly to avoid middleware conflict."""
if not file_path:
file_path = "index.html"
if not file_path or file_path == "index.html":
return RedirectResponse(url="/static/main.html")
full_path = os.path.join(STATIC_DIR, file_path)
if os.path.isfile(full_path):
return FileResponse(full_path)
......@@ -133,7 +136,12 @@ app.include_router(prompt_optimizer_router) # Prompt Optimizer
app.include_router(user_simulator_router) # User Simulator
app.include_router(regression_test_router) # Regression Test
app.include_router(stress_test_router) # Stress Test
app.include_router(roadmap_flow_router) # Roadmap & Flow
app.include_router(experiment_log_router) # Experiment Log
app.include_router(auth_router) # Auth (login/me/logout)
app.include_router(product_desc_router) # Ultra Description Manager
from api.limit_route import router as limit_router
app.include_router(limit_router)
if __name__ == "__main__":
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
{"mua": "Mùa xuân – Hè", "tags": "áo phông, trẻ em, unisex, stitch, hoạt hình, cotton", "layer": "Khi thời tiết se lạnh, có thể khoác nhẹ áo khoác gió hoặc áo khoác chần bông để giữ ấm mà không làm mất đi vẻ trẻ trung.", "dip_mac": "Đi học hàng ngày · Dạo phố cuối tuần · Kỳ nghỉ hè · Sinh nhật bạn bè", "do_tuoi": "5-12 tuổi", "faq_1_a": "Form regular fit và phần vai hơi rộng giúp che nhẹ bắp tay, tạo cảm giác thoải mái cho bé có vòng ngực trung bình đến hơi to.", "faq_1_q": "Áo này có phù hợp với bé có vòng ngực hơi to không?", "faq_2_a": "Theo bảng size, chiều cao 110 cm tương ứng size 110; nếu muốn áo hơi rộng hơn để di chuyển thoải mái, có thể chọn size 116.", "faq_2_q": "Bé 110 cm nên chọn size nào?", "faq_3_a": "Kết hợp với áo khoác gió nhẹ hoặc áo khoác chần bông màu trung tính (be, xám) sẽ giữ ấm và vẫn giữ được nét hoạt hình sinh động của áo.", "faq_3_q": "Có thể phối áo này với áo khoác nào để tạo phong cách mùa thu?", "phoi_do": "Quần jean + Giày sneaker trắng → Năng động chơi phố | Quần khaki + Dép xỏ ngón → Gọn gàng dạo công viên | Quần legging + Giày slip‑on đen → Thoải mái sau giờ học", "tagline": "Stitch đồng hành, bé yêu tỏa sáng mỗi ngày", "loi_song": "Thích khám phá, yêu thiên nhiên và những nhân vật hoạt hình", "chat_lieu": "Không xác định", "gioi_tinh": "Unisex", "ly_do_mua": "Thiết kế độc đáo, chất liệu thoáng mát, phù hợp cho mọi hoạt động trẻ em, dễ dàng mix‑match để tạo phong cách riêng.", "tinh_cach": "Hòa đồng, vui tươi, luôn tràn đầy năng lượng", "cross_sell": "Quần jean, Áo khoác gió, Bộ thể thao", "luu_y_size": "Hãy đo chiều cao bé và so sánh với bảng size; nếu bé ở mức trung gian giữa hai size, nên chọn size lớn hơn để áo không bị bó.", "phong_cach": "Casual, hoạt hình", "mo_ta_chinh": "Form dáng regular fit nhẹ nhàng, không gò bó, giúp bé tự tin di chuyển. Thiết kế in hình Stitch đáng yêu cùng chữ “SEA YOU LATER” tạo điểm nhấn hoạt hình sinh động. Chiếc áo này làm dịu mắt, khiến làn da bé luôn thoải mái và nổi bật trong mọi hoạt động.", "ten_san_pham": "Áo phông unisex trẻ em in hình Stitch", "tinh_nang_vai": "Co giãn nhẹ, thấm hút mồ hôi tốt, giữ form sau nhiều lần giặt", "hook_quang_cao": "Stitch cùng bé khám phá thế giới, mỗi bước đi là một câu chuyện vui nhộn!", "tranh_phoi_cung": "Không nên ghép áo phông hoạt hình với áo blazer trang trọng hoặc quần tây công sở vì sẽ phá vỡ tính năng vui tươi; tránh kết hợp cùng áo khoác nỉ dày vào mùa hè vì sẽ làm bé cảm thấy nóng bức.", "huong_dan_bao_quan": "Giặt máy ≤30°C, lộn trái, không dùng chất tẩy, phơi trong bóng râm để bảo màu sắc sáng bóng", "nguyen_tac_phoi_do": "Áo phông rộng vừa giúp cân bằng tỉ lệ với quần jean ôm sát, tạo dáng cân đối; màu sắc tươi sáng của áo sẽ nổi bật hơn khi kết hợp với quần khaki trung tính; quần legging co giãn giúp bé thoải mái vận động, đồng thời giữ cho áo không bị xê dịch, thích hợp cho các hoạt động năng động."}
# Init file for worker package
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment