Các Lệnh Client Gửi Lên Server
Tổng Hợp Lệnh
| type | Mô tả |
|---|---|
chat | Bắt đầu lượt hội thoại mới |
subscribe | Đăng ký nhận events của session |
stop | Dừng agent đang chạy |
push_message | Xếp hàng message khi agent đang bận |
approval | Phê duyệt tool call hoặc trả lời câu hỏi |
ping / pong | Keep-alive |
chat — Bắt Đầu Lượt Hội Thoại
{
"type": "chat",
"message": "Phân tích cổ phiếu VNM",
"session_id": "uuid-session-id",
"model": "claude-sonnet-4-6",
"agent_id": "financial_agent",
"content_urls": [
"https://example.com/bao-cao.pdf"
],
"agent_config": {
"display_mode": "agent"
}
}
| Field | Bắt buộc | Mô tả |
|---|---|---|
message | Có | Nội dung tin nhắn người dùng |
session_id | Không | Bỏ trống để tạo session mới |
model | Không | Override model (mặc định theo cấu hình server) |
agent_id | Không | Override agent |
content_urls | Không | Danh sách URL file/ảnh đính kèm |
agent_config.display_mode | Không | "agent" (mặc định) hoặc "chat" |
display_mode: "agent" — hiển thị thinking, tool calls, toàn bộ quá trình suy luận.
display_mode: "chat" — chỉ trả về text answer, không có thinking hay tool call events.
Session Mới
Khi không có session_id, server tạo session mới và trả về trong subscribed.session_id. Client phải lưu lại ID này để dùng cho các lượt tiếp theo.
Response Sau chat
{
"type": "subscribed",
"session_id": "abc-123",
"status": "running"
}
Sau đó server bắt đầu stream toàn bộ events của lượt (xem Event Stream).
Khi Agent Đang Bận
Nếu session đã có task đang chạy, message được tự động xếp hàng:
{
"type": "message_queued",
"session_id": "abc-123"
}
subscribe — Đăng Ký Nhận Events
Dùng khi reconnect hoặc muốn nhận lại events từ một điểm cụ thể.
{
"type": "subscribe",
"session_id": "abc-123",
"last_event_id": "01HXYZ..."
}
| Field | Bắt buộc | Mô tả |
|---|---|---|
session_id | Có | Session cần subscribe |
last_event_id | Không | Replay từ event ID này. Bỏ trống để nhận toàn bộ |
Response
{
"type": "subscribed",
"session_id": "abc-123",
"status": "running"
}
hoặc:
{
"type": "subscribed",
"session_id": "abc-123",
"status": "completed"
}
status: "running"→ server replay events còn thiếu + tiếp tục stream livestatus: "completed"→ server replay events còn thiếu rồi dừng
Lỗi Session Không Tồn Tại
{
"type": "error",
"code": "SESSION_NOT_FOUND",
"message": "Session not found or no active stream"
}
stop — Dừng Agent
{
"type": "stop",
"session_id": "abc-123"
}
Server đặt stop signal. Agent dừng ở lần kiểm tra tiếp theo (thường < 1 giây).
Sau khi dừng, server sẽ emit:
- Block
terminal_user_stopped message_deltamessage_stop
Không có ACK trực tiếp — message_stop là xác nhận cuối cùng.
Nếu không có task đang chạy:
{
"type": "error",
"code": "SESSION_NOT_RUNNING",
"message": "No running task for this session"
}
push_message — Xếp Hàng Message
{
"type": "push_message",
"session_id": "abc-123",
"message": "Cũng phân tích thêm cổ phiếu HPG"
}
Message được xử lý ngay sau khi lượt hiện tại hoàn thành.
{
"type": "message_queued",
"session_id": "abc-123"
}
Bảng Mã Lỗi
| code | Khi nào |
|---|---|
INVALID_JSON | Message không phải JSON hợp lệ |
UNKNOWN_TYPE | Trường type không xác định |
EMPTY_MESSAGE | chat không có message |
MISSING_SESSION_ID | subscribe không có session_id |
SESSION_NOT_FOUND | Session không tồn tại hoặc đã hết TTL |
SESSION_NOT_RUNNING | stop / push_message khi không có task |
TASK_START_FAILED | Lỗi nội bộ khi khởi động task |
Ví Dụ: React.js
function ChatInput({ sessionId, ws, onSessionCreated }) {
const [message, setMessage] = useState('')
const sendMessage = () => {
if (!message.trim()) return
ws.send(JSON.stringify({
type: 'chat',
message: message.trim(),
session_id: sessionId || undefined,
agent_config: { display_mode: 'agent' },
}))
setMessage('')
}
const stopAgent = () => {
if (!sessionId) return
ws.send(JSON.stringify({ type: 'stop', session_id: sessionId }))
}
return (
<div>
<input value={message} onChange={e => setMessage(e.target.value)} />
<button onClick={sendMessage}>Gửi</button>
<button onClick={stopAgent}>Dừng</button>
</div>
)
}
Ví Dụ: Flutter (Dart)
class ChatService {
final AgentWebSocket _ws;
String? _sessionId;
ChatService(this._ws);
void sendMessage(String message) {
final payload = {
'type': 'chat',
'message': message,
if (_sessionId != null) 'session_id': _sessionId,
'agent_config': {'display_mode': 'agent'},
};
_ws.send(payload);
}
void stop() {
if (_sessionId == null) return;
_ws.send({'type': 'stop', 'session_id': _sessionId});
}
void pushMessage(String message) {
if (_sessionId == null) return;
_ws.send({
'type': 'push_message',
'session_id': _sessionId,
'message': message,
});
}
void handleSubscribed(Map<String, dynamic> msg) {
_sessionId = msg['session_id'] as String;
// Lưu vào local storage để dùng khi reconnect
SharedPreferences.getInstance()
.then((prefs) => prefs.setString('last_session_id', _sessionId!));
}
}