420 lines
11 KiB
Markdown
420 lines
11 KiB
Markdown
# 🤖 LiteRT-LM Web Server
|
||
[English](README-en.md)
|
||
|
||
|
||
Chạy mô hình **Gemma 4** trên thiết bị nhúng (Orange Pi 5, Raspberry Pi, v.v.) thông qua [LiteRT-LM](https://github.com/google-ai-edge/litert-lm) với giao diện REST API và Web UI.
|
||
|
||
---
|
||
|
||
## 📋 Yêu cầu
|
||
|
||
- Python 3.10+
|
||
- [`litert-lm`](https://github.com/google-ai-edge/litert-lm) đã cài và hoạt động
|
||
- Thư viện Python:
|
||
|
||
```bash
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
`requirements.txt`:
|
||
```
|
||
fastapi
|
||
uvicorn
|
||
pydantic
|
||
huggingface_hub
|
||
```
|
||
|
||
---
|
||
|
||
## 📁 Cấu trúc
|
||
|
||
```
|
||
.
|
||
├── app.py # REST API đơn giản, single-turn
|
||
├── server.py # REST API đầy đủ + Web UI, multi-turn sessions
|
||
├── templates/
|
||
│ └── index.html # Giao diện Web UI (tách riêng khỏi server.py)
|
||
├── models/ # Thư mục chứa các file model .litertlm
|
||
│ ├── gemma-4-E2B-it.litertlm
|
||
│ └── gemma-4-E4B-it.litertlm
|
||
├── requirements.txt
|
||
└── README.md
|
||
```
|
||
|
||
---
|
||
|
||
## 🤖 Models hỗ trợ
|
||
|
||
| Model | Repo HuggingFace | Mô tả |
|
||
|-------|-----------------|-------|
|
||
| `gemma-4-E2B-it` | [litert-community/gemma-4-E2B-it-litert-lm](https://huggingface.co/litert-community/gemma-4-E2B-it-litert-lm) | Edge 2B — nhanh hơn, nhẹ hơn |
|
||
| `gemma-4-E4B-it` | [litert-community/gemma-4-E4B-it-litert-lm](https://huggingface.co/litert-community/gemma-4-E4B-it-litert-lm) | Edge 4B — thông minh hơn, nặng hơn |
|
||
|
||
> **Lưu ý:** Nên dùng bản `-it` (instruction-tuned) cho chat/hỏi đáp. Bản không có `-it` là base model, chỉ predict token tiếp theo, không phù hợp để hội thoại.
|
||
|
||
### Tải model về
|
||
|
||
```bash
|
||
# Gemma 4 E2B (nhỏ hơn, ~nhanh hơn)
|
||
hf download litert-community/gemma-4-E2B-it-litert-lm \
|
||
--include '*.litertlm' \
|
||
--local-dir models/
|
||
|
||
# Gemma 4 E4B (lớn hơn, ~thông minh hơn)
|
||
hf download litert-community/gemma-4-E4B-it-litert-lm \
|
||
--include '*.litertlm' \
|
||
--local-dir models/
|
||
```
|
||
|
||
> **Hoặc** để server tự động tải khi chọn model chưa có sẵn.
|
||
|
||
---
|
||
|
||
## 🚀 Hướng dẫn sử dụng
|
||
|
||
### Cách 1: Chạy với tùy chọn mặc định
|
||
|
||
```bash
|
||
python server.py
|
||
```
|
||
|
||
Server sẽ hiển thị menu **chọn model** trước khi khởi động:
|
||
|
||
```
|
||
====================================================
|
||
LiteRT-LM Server — Chọn model
|
||
====================================================
|
||
[1] gemma-4-E2B-it
|
||
Gemma 4 Edge 2B — nhỏ hơn, nhanh hơn
|
||
✓ có sẵn
|
||
|
||
[2] gemma-4-E4B-it
|
||
Gemma 4 Edge 4B — thông minh hơn, chậm hơn
|
||
✗ chưa tải
|
||
|
||
[3] Sử dụng model từ đường dẫn khác
|
||
|
||
Chọn model (1/2/3):
|
||
```
|
||
|
||
**Tính năng tự động tải model:**
|
||
- Nếu chọn model chưa có, server sẽ hỏi: `Bạn muốn tải model ngay bây giờ? (y/n)`
|
||
- Chọn `y` để tự động tải từ Hugging Face
|
||
- Hoặc chọn `n` để tải thủ công sau
|
||
|
||
**Tự động xử lý port:**
|
||
- Nếu port 8000 đã bị chiếm, server sẽ hỏi chọn port khác
|
||
- Hoặc nhấn Enter để tự động tìm port khả dụng (8001-8999)
|
||
|
||
### Cách 2: Chạy với tham số command line
|
||
|
||
```bash
|
||
# Chỉ định port
|
||
python server.py --port 8080
|
||
|
||
# Chỉ định model từ đường dẫn
|
||
python server.py --model /path/to/model.litertlm
|
||
|
||
# Kết hợp cả hai
|
||
python server.py --port 8080 --model ~/models/gemma-4-E2B-it.litertlm
|
||
|
||
# Xem hướng dẫn đầy đủ
|
||
python server.py --help
|
||
```
|
||
|
||
### Mở Web UI
|
||
|
||
```
|
||
http://<địa-chỉ-ip>:<port>
|
||
```
|
||
|
||
Tên model và port sẽ hiển thị khi server khởi động:
|
||
|
||
```
|
||
====================================================
|
||
🚀 Server đang khởi động...
|
||
📍 URL: http://localhost:8000
|
||
📦 Model: gemma-4-E2B-it.litertlm
|
||
====================================================
|
||
```
|
||
|
||
---
|
||
|
||
## 📄 `app.py` — REST API đơn giản
|
||
|
||
File cơ bản, single-turn, không có menu chọn model. Phù hợp để tích hợp nhanh hoặc test.
|
||
|
||
### Chạy
|
||
|
||
```bash
|
||
python app.py
|
||
```
|
||
|
||
### Endpoint
|
||
|
||
#### `POST /generate`
|
||
|
||
Gửi một prompt, nhận phản hồi. Mỗi request là độc lập, **không có bộ nhớ** giữa các lần gọi.
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8000/generate \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"prompt": "Bạn là ai?"}'
|
||
```
|
||
|
||
**Response:**
|
||
|
||
```json
|
||
{
|
||
"response": "Tôi là Gemma 4, một Mô hình Ngôn ngữ Lớn...",
|
||
"tokens": 42,
|
||
"elapsed_s": 5.31,
|
||
"tokens_per_sec": 7.91
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🖥️ `server.py` — REST API đầy đủ + Web UI
|
||
|
||
Phiên bản đầy đủ với chọn model khi khởi động, multi-turn conversation, quản lý session và giao diện chat trên trình duyệt.
|
||
|
||
---
|
||
|
||
### 🌐 Web UI
|
||
|
||
Mở trình duyệt và truy cập `http://<địa-chỉ-ip>:8000`
|
||
|
||
Tính năng:
|
||
- **Chọn model khi khởi động** qua menu CLI — tên model hiển thị trực tiếp trên header
|
||
- Giao diện chat trực quan, hỗ trợ tiếng Việt
|
||
- Tự động tạo session khi mở trang
|
||
- Nhớ ngữ cảnh hội thoại trong cùng một session
|
||
- Nút **New** để bắt đầu cuộc trò chuyện mới
|
||
- Nút **Clear** để xóa lịch sử và tạo session mới
|
||
- `Enter` để gửi, `Shift + Enter` để xuống dòng
|
||
- **Render Markdown**: câu trả lời hiển thị đúng định dạng (heading, list, code block, table, bold/italic, v.v.)
|
||
- **Đo tốc độ**: badge `⚡ X tok/s` bên dưới mỗi câu trả lời, kèm số token và thời gian xử lý
|
||
|
||
---
|
||
|
||
### 🔌 REST API
|
||
|
||
#### `GET /info`
|
||
Trả về thông tin model đang chạy và số session hiện tại.
|
||
|
||
```bash
|
||
curl http://localhost:8000/info
|
||
```
|
||
|
||
**Response:**
|
||
|
||
```json
|
||
{
|
||
"model": "gemma-4-E2B-it",
|
||
"sessions": 2
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### `POST /generate`
|
||
Single-turn, không nhớ context. Dùng khi chỉ cần hỏi đáp đơn lẻ.
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8000/generate \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"prompt": "Thủ đô của Việt Nam là gì?"}'
|
||
```
|
||
|
||
**Response:**
|
||
|
||
```json
|
||
{
|
||
"response": "Thủ đô của Việt Nam là Hà Nội.",
|
||
"tokens": 12,
|
||
"elapsed_s": 1.45,
|
||
"tokens_per_sec": 8.27
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### `POST /chat/new`
|
||
Tạo một session mới. Trả về `session_id` dùng cho các request tiếp theo.
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8000/chat/new
|
||
```
|
||
|
||
**Response:**
|
||
|
||
```json
|
||
{
|
||
"session_id": "a3f2c1d4-..."
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### `POST /chat/{session_id}`
|
||
Gửi tin nhắn trong một session. Model **nhớ toàn bộ lịch sử hội thoại** trong session đó.
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8000/chat/a3f2c1d4-... \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"prompt": "Kể thêm về điều đó đi"}'
|
||
```
|
||
|
||
**Response:**
|
||
|
||
```json
|
||
{
|
||
"session_id": "a3f2c1d4-...",
|
||
"response": "...",
|
||
"tokens": 58,
|
||
"elapsed_s": 7.12,
|
||
"tokens_per_sec": 8.15
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### `DELETE /chat/{session_id}`
|
||
Xóa session và giải phóng bộ nhớ.
|
||
|
||
```bash
|
||
curl -X DELETE http://localhost:8000/chat/a3f2c1d4-...
|
||
```
|
||
|
||
**Response:**
|
||
|
||
```json
|
||
{
|
||
"status": "cleared",
|
||
"session_id": "a3f2c1d4-..."
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
#### `GET /chat/sessions/list`
|
||
Liệt kê tất cả session đang hoạt động.
|
||
|
||
```bash
|
||
curl http://localhost:8000/chat/sessions/list
|
||
```
|
||
|
||
**Response:**
|
||
|
||
```json
|
||
{
|
||
"sessions": ["a3f2c1d4-...", "b7e9f2a1-..."],
|
||
"count": 2
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 💡 Ví dụ: Multi-turn conversation qua curl
|
||
|
||
```bash
|
||
# 1. Tạo session
|
||
SESSION=$(curl -s -X POST http://localhost:8000/chat/new | python3 -c "import sys,json; print(json.load(sys.stdin)['session_id'])")
|
||
|
||
# 2. Gửi tin nhắn đầu tiên
|
||
curl -s -X POST http://localhost:8000/chat/$SESSION \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"prompt": "Tên tôi là Nam"}' | python3 -m json.tool
|
||
|
||
# 3. Model nhớ context
|
||
curl -s -X POST http://localhost:8000/chat/$SESSION \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"prompt": "Tên tôi là gì?"}' | python3 -m json.tool
|
||
|
||
# 4. Xóa session khi xong
|
||
curl -X DELETE http://localhost:8000/chat/$SESSION
|
||
```
|
||
|
||
---
|
||
|
||
## ⚙️ Cấu hình
|
||
|
||
### Tham số command line
|
||
|
||
| Tham số | Mô tả | Mặc định |
|
||
|---------|-------|---------|
|
||
| `--port`, `-p` | Port để chạy server | `8000` |
|
||
| `--model`, `-m` | Đường dẫn đầy đủ tới file model .litertlm | None (chọn từ menu) |
|
||
| `--help`, `-h` | Hiển thị hướng dẫn | - |
|
||
|
||
### Cấu hình trong code
|
||
|
||
Các tham số chỉnh trong đầu `server.py`:
|
||
|
||
| Biến | Mô tả | Mặc định |
|
||
|------|-------|---------|
|
||
| `MODELS_DIR` | Thư mục chứa model | `./models` |
|
||
| `AVAILABLE_MODELS` | Danh sách model + repo HuggingFace | xem trong file |
|
||
| `backend` | Backend inference | `litert_lm.Backend.CPU` |
|
||
| `host` | Địa chỉ lắng nghe | `0.0.0.0` |
|
||
|
||
Để thêm model mới vào menu, bổ sung vào dict `AVAILABLE_MODELS` trong `server.py`:
|
||
|
||
```python
|
||
AVAILABLE_MODELS = {
|
||
"gemma-4-E2B-it": {
|
||
"file": "gemma-4-E2B-it.litertlm",
|
||
"repo": "litert-community/gemma-4-E2B-it-litert-lm",
|
||
"desc": "Gemma 4 Edge 2B — nhỏ hơn, nhanh hơn",
|
||
},
|
||
"ten-model-moi": {
|
||
"file": "ten-model-moi.litertlm",
|
||
"repo": "org/repo-name",
|
||
"desc": "Mô tả model",
|
||
},
|
||
}
|
||
```
|
||
|
||
Để đổi backend sang GPU (nếu thiết bị hỗ trợ):
|
||
|
||
```python
|
||
engine = litert_lm.Engine(str(MODEL_PATH), backend=litert_lm.Backend.GPU)
|
||
```
|
||
|
||
### Chạy như systemd service (Linux)
|
||
|
||
Xem hướng dẫn chi tiết trong [SERVICE_README.md](SERVICE_README.md)
|
||
|
||
```bash
|
||
# Cài đặt service
|
||
sudo bash install_service.sh
|
||
|
||
# Quản lý service
|
||
sudo systemctl status litert-lm
|
||
sudo systemctl restart litert-lm
|
||
sudo journalctl -u litert-lm -f
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 Ghi chú
|
||
|
||
- Mỗi session giữ toàn bộ lịch sử hội thoại trong RAM. Nên xóa session khi không dùng nữa.
|
||
- Warning `mel_filterbank` khi khởi động là bình thường — liên quan đến audio encoder của Gemma 4 multimodal, không ảnh hưởng đến text generation.
|
||
- Tốc độ generate phụ thuộc vào phần cứng. Trên Orange Pi 5 với CPU, khoảng 5–15 token/giây.
|
||
- Token/s dùng `engine.tokenize()` nếu có, fallback về ước tính `len(text) // 4` nếu không khả dụng.
|
||
- Markdown được render bằng [marked.js](https://marked.js.org/) trực tiếp trên trình duyệt, không qua server.
|
||
- Chỉ dùng bản `-it` (instruction-tuned) cho chat — bản base model không phù hợp hội thoại.
|
||
|
||
---
|
||
|
||
## 📜 Bản quyên
|
||
|
||
Copyright (c) 2026
|
||
|
||
[Trần Thanh Tân / [TTAI Solutions Software](https://ttaisolutions.com/)]
|
||
|
||
All rights reserved.
|
||
|
||
Không được phép sao chép, phân phối hoặc truyền tải bất kỳ phần nào của phần mềm này hoặc mã nguồn của nó dưới bất kỳ hình thức hoặc phương tiện nào, bao gồm sao chụp, ghi âm hoặc các phương pháp điện tử hoặc cơ khí khác, mà không có sự cho phép bằng văn bản trước của chủ sở hữu bản quyền. |